<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nicholas</title>
    <description>The latest articles on DEV Community by Nicholas (@eren_poger).</description>
    <link>https://dev.to/eren_poger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3993828%2Fc19a7c67-2e75-4dc0-85ea-b43cde21a54e.jpg</url>
      <title>DEV Community: Nicholas</title>
      <link>https://dev.to/eren_poger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eren_poger"/>
    <language>en</language>
    <item>
      <title>A long break from post, but I’ve cocked some stuff like Walter White did.</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Wed, 12 Aug 2026 19:11:00 +0000</pubDate>
      <link>https://dev.to/eren_poger/a-long-break-from-post-but-ive-cocked-some-stuff-like-walter-white-did-41ad</link>
      <guid>https://dev.to/eren_poger/a-long-break-from-post-but-ive-cocked-some-stuff-like-walter-white-did-41ad</guid>
      <description>&lt;p&gt;Hey guys ;)&lt;br&gt;
It’s been a while since my last post. Actually I was a bit busy and forgot to write about progress. And this time I really have what to say. So what happened in short?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scraper ended data extending.&lt;/li&gt;
&lt;li&gt;Pageviews script added pageview info for all 6k books.&lt;/li&gt;
&lt;li&gt;My first analysis notebook is done.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;So, here is a fragment of my extended data before pageviews.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3702itqg4b33ctb9t84k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3702itqg4b33ctb9t84k.png" alt="Extended data" width="799" height="437"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;And here is a fragment of dataset &lt;strong&gt;after pageviews&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvc76cdy2blzdtqtmci1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvc76cdy2blzdtqtmci1.png" alt="Pageviews extending" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Is pageview extending false?
&lt;/h2&gt;

&lt;p&gt;As I said in a previous post, sometimes Wikipedia pageviews give some nonsense data, and it’s true. Barely it gives an exact match, more often it gives the author page, and most of the time it gives 0 or some nonsense. &lt;br&gt;
When I’ll start my second analysis notebook, &lt;strong&gt;I’ll do something about that&lt;/strong&gt;, &lt;em&gt;maybe erase some data, for example if the saved Wiki title is in author or in title, I don’t know yet&lt;/em&gt;.&lt;br&gt;
Here is my pageview script fragment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fktbzfoudyyniizhgz3xa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fktbzfoudyyniizhgz3xa.png" alt="Pageviews code fragment" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fww1byqjeq8jbkkwx4g97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fww1byqjeq8jbkkwx4g97.png" alt="Pageviews code fragment" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  First conclusions
&lt;/h2&gt;

&lt;p&gt;When I began this project I asked a question, do people read less nowadays? Actually I don’t know. According to my data I couldn’t say that people read less or people read more nowadays. All I could say it’s that people still read some books indeed. &lt;br&gt;
&lt;strong&gt;Again all ,my conclusion here will be true according to my scrapped data, custom standard of books popularity and validity for Ukraine reading culture.&lt;/strong&gt; &lt;br&gt;
So, people prefer physical books more, the most popular author is Hiro Mashima, because of the number of his manga volumes. The most popular publishers are those who produce manga and comics, again because of the number of volumes and parts. The amount of books released in the last 10 years is bigger than the amount of books released 10-20+ years ago. And the most popular categories are manga, comics, autobiography and memoirs, self-improvement and children literature. Charts from notebook I’ll add too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94b5wlqp6dwbzuxt6trr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94b5wlqp6dwbzuxt6trr.png" alt="Format chart" width="483" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvfkb64qy1ewa3wb90s5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvfkb64qy1ewa3wb90s5g.png" alt="Authors chart" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevdk3ea52hznllnekr4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevdk3ea52hznllnekr4f.png" alt="Publishers chart" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n4j73ioywfyc8cdigaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n4j73ioywfyc8cdigaj.png" alt="Release year chart" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fppbb2iykxu0ab6wnq0ri.png" alt="Categories chart" width="800" height="709"&gt;
&lt;/h2&gt;

&lt;p&gt;What do I mean by using the words popular or famous? So in my dataset there are 6k books. And for each analysis I’ve used count. So the most popular author is who has the most amount of books. And I should remind you, that it isn’t some 6k books, it's the best books according to my custom standard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;I think the next step is going to be the last. I don’t know, maybe I’ll do all the code tomorrow, or next week. So what do I need to do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyse books, their popularity according to standard and to pageviews.&lt;/li&gt;
&lt;li&gt;Write something between code.&lt;/li&gt;
&lt;li&gt;Write final conclusion and README.md (with some screenshots to show what this project is about very quickly, without opening notebooks).&lt;/li&gt;
&lt;li&gt;Push and commit everything.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>books</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>What do marketing tags, broken author arrays, and an unexpected email from Derek Sivers have in common?</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 01 Aug 2026 13:38:41 +0000</pubDate>
      <link>https://dev.to/eren_poger/what-do-marketing-tags-broken-author-arrays-and-an-unexpected-email-from-derek-sivers-have-in-165c</link>
      <guid>https://dev.to/eren_poger/what-do-marketing-tags-broken-author-arrays-and-an-unexpected-email-from-derek-sivers-have-in-165c</guid>
      <description>&lt;p&gt;Hey guys 👋&lt;/p&gt;

&lt;p&gt;I don't have a massive technical feature to showcase today, but I do have a solid batch of data pipeline updates, a bug fix story, and a pretty inspiring personal highlight.&lt;/p&gt;

&lt;p&gt;While refining the secondary parser for the Ukrainian book market, I ran into classic data pollution issues, stripped out marketing noise, and got a reminder of why building side projects should be fun.&lt;/p&gt;

&lt;p&gt;Here is the weekly progress report!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the "Comma-in-Category" Bug
&lt;/h2&gt;

&lt;p&gt;After rewriting the extended scraper, I noticed a subtle data formatting bug during early inspection.&lt;/p&gt;

&lt;p&gt;Certain books on retail platforms list multiple authors. My initial DOM selection logic grabbed only the primary author string. However, because of how the HTML elements were structured, the trailing author separators (commas and secondary tags) bled directly into the Category column!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Corrupted: Category = ", Fiction, Bestsellers"
Cleaned:   Category = "Fiction"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated the scraper logic to properly parse multi-author arrays.&lt;/li&gt;
&lt;li&gt;Manually sanitized and patched 20 affected JSON objects in the clean dataset to keep data integrity intact.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stripping "Garbage" Categories &amp;amp; Preprocessing
&lt;/h2&gt;

&lt;p&gt;I’m currently finishing up the final data preparation script, specifically handling empty values (&lt;code&gt;NaN&lt;/code&gt;s) and filtering non-standard categories.&lt;/p&gt;

&lt;p&gt;Originally, I expected platform categories to function purely as literary sub-genres (e.g., Sci-Fi, Historical Fiction, Biography). However, e-commerce platforms heavily mix genre metadata with promotional marketing tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"Reader's Choice"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Top Bestsellers 2023"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Must-Read Recommendations"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tags provide zero value when trying to analyze actual genre trends over time. My preprocessing script is currently dropping these noise categories along with empty rows to leave a pure, genre-focused analytical dataset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzdt3sib88yyo4jrzkpcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzdt3sib88yyo4jrzkpcm.png" alt="Code for cleaning categories" width="800" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyt74sf5m2ngv1sv4zdhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyt74sf5m2ngv1sv4zdhf.png" alt="Jsonl fragment" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  An Unexpected Reply from Derek Sivers
&lt;/h2&gt;

&lt;p&gt;On a non-technical note, something really cool happened this week.&lt;/p&gt;

&lt;p&gt;I recently finished reading &lt;em&gt;Anything You Want&lt;/em&gt; by Derek Sivers-a short, brilliant book about entrepreneurship, independence, and doing things your own way. At the end of the book, Derek invites readers to send him an email, introduce themselves, and share what they are working on.&lt;/p&gt;

&lt;p&gt;I decided to write to him, not really expecting a reply. To my surprise, he actually responded! He shared some thoughtful feedback and even recommended a few more great books to check out.&lt;/p&gt;

&lt;p&gt;It was a great reminder that building side projects-whether it's a Python chess engine or a market scraper-should come from a place of curiosity and freedom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuvv0xi3rsbguo3aj68pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuvv0xi3rsbguo3aj68pn.png" alt="Email from Derek" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Finishing the final &lt;code&gt;NaN&lt;/code&gt; drop and category mapping pass in Pandas.&lt;/li&gt;
&lt;li&gt;Running exploratory data analysis (EDA) to generate our very first trend charts.&lt;/li&gt;
&lt;li&gt;Starting one of Derek's recommended books during my 3-day rest period!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you ever reached out directly to an author or creator who inspired you? How do you handle promotional marketing tags when cleaning e-commerce data? Let's chat in the comments below! 👇&lt;/p&gt;

</description>
      <category>python</category>
      <category>pandas</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
    <item>
      <title>No APIs?! Analysis is almost here, but I’ve mistaken again.</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Wed, 29 Jul 2026 14:56:14 +0000</pubDate>
      <link>https://dev.to/eren_poger/no-apis-analysis-is-almost-here-but-ive-mistaken-again-54k0</link>
      <guid>https://dev.to/eren_poger/no-apis-analysis-is-almost-here-but-ive-mistaken-again-54k0</guid>
      <description>&lt;p&gt;"Why hit API request limits for 111,000 books when a single log10 formula and direct web scraping can solve the problem?"&lt;/p&gt;

&lt;p&gt;Hey guys ;)&lt;/p&gt;

&lt;p&gt;Looks like my focus is completely locked on data analysis right now. I can’t help it-this project is just too fun.&lt;/p&gt;

&lt;p&gt;Over the past two or three posts, I kept promising that I would enrich my book dataset using the Google Books API and OpenLibrary. Well... forget about the APIs.&lt;/p&gt;

&lt;p&gt;After wrestling with rate limits, poor metadata quality, and HTML structures all week, I dropped the APIs entirely and built my own data enrichment pipeline. Here is how it happened, why I deleted 1,451 scraped books today, and what’s coming next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem with Books APIs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I started planning the data enrichment phase for my 111,000 unique scraped books, I ran into two massive brick walls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Books API Limits:&lt;/strong&gt; The free tier caps you at 1,000 requests per day. At that rate, enriching 111k books would take over three months (I know about payment account and increasing of limits).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenLibrary Limitations:&lt;/strong&gt; It handles Ukrainian literature poorly without exact ISBN matches. Title and Author search combinations gave way too many false positives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I quickly realized I didn't actually need all 111,000 books. Thousands of them had zero reviews, no ratings, and no active search interest. Processing them was just adding noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filtering the Catalog and Popularity Formula&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To isolate the most relevant titles, I designed a custom metric to score and rank the entire catalog:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;popularity_rating = log10(amount_of_reviews + 1) * rating&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Using the logarithmic scale prevents titles with hundreds of reviews from completely breaking the ranking curve while still giving heavy weight to actual reader engagement.&lt;/p&gt;

&lt;p&gt;This formula immediately isolated ~3,000 highly active titles. I expanded the threshold to capture the top 6,000 books for the final analytical sample.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parsing Metadata Directly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To analyze publishing trends over time, I needed the exact publication year for a specific edition, not just the general first release year of a work.&lt;/p&gt;

&lt;p&gt;To get that accuracy from APIs, I would need to parse the book's individual URL anyway to extract its ISBN. That's when the realization hit me: If I'm already scraping the specific book pages for ISBNs, why not just scrape the missing metadata directly from Yakaboo?&lt;/p&gt;

&lt;p&gt;So from Monday until yesterday, I wrote a dedicated secondary parser to process my top 6,000 URL list, extract extended properties, and map them into structured category buckets.&lt;/p&gt;

&lt;p&gt;Here is my best books selection code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqt7kctazp3xfxmvxkhsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqt7kctazp3xfxmvxkhsg.png" alt="Best books selection" width="799" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is scraper fragment&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe49xyn6za1cl0jhe92qw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe49xyn6za1cl0jhe92qw.png" alt="Scraper fragment" width="799" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And how I deal with categories for final dataset preprocessing&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bs5vhls7ny6gaaiwg7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bs5vhls7ny6gaaiwg7b.png" alt="Categories preprocessing" width="799" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The HTML Trap and deleting 1,451 Books&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It wouldn't be a real project without a good mistake.&lt;/p&gt;

&lt;p&gt;While running the extended parser, I noticed that Yakaboo's HTML structure reuses generic CSS classes across different metadata blocks while hiding or omitting unique element IDs. Because of a misplaced selector logic, my parser was extracting Author names and saving them into the Publisher field. 😅&lt;/p&gt;

&lt;p&gt;After 1,451 books were parsed, I caught the bug, wiped the corrupted JSON file, and fixed the DOM selector.&lt;/p&gt;

&lt;p&gt;Today, I’m re-running the script on the top 6,000 clean URLs. The most important lesson in Data Engineering: never give up, even when you have to wipe your output and start over!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary &amp;amp; What's Next&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; All changes are finally committed and pushed! I'm much more cautious here than in my Chess project, pushing only when the pipeline logic is 100% verified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current Task:&lt;/strong&gt; Re-running the extended parser for the top 6k books without the Author/Publisher swap bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next Goal:&lt;/strong&gt; Data cleaning, category normalization, and generating our first analytical charts to answer if people are reading fewer books over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you ever built a custom metric to downsample a massive dataset? What’s your worst HTML scraping horror story? Let’s chat in the comments! 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#python #datascience #webscraping #pandas #showdev&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>pandas</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Weekends with translator for book analysis</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 25 Jul 2026 11:29:37 +0000</pubDate>
      <link>https://dev.to/eren_poger/weekends-with-translator-for-book-analysis-21bm</link>
      <guid>https://dev.to/eren_poger/weekends-with-translator-for-book-analysis-21bm</guid>
      <description>&lt;p&gt;Hey guys ;)&lt;/p&gt;

&lt;p&gt;Today I’m going to tell you about some progress in books analysis and my plans for the weekend. Let’s start.&lt;/p&gt;

&lt;p&gt;So from my previous post, I’ve been working with the author translation problem. What is it? Okay, so for extending books data I should use some API, and for that I need an ISBN that will give a certain book with 99% accuracy, or use a Title + Author combination. When a book is from a Ukrainian publisher, it will find the book with 80-90% accuracy. But what happens if the book is from a foreign publisher? Well, in fact, the accuracy will definitely be less.&lt;/p&gt;

&lt;p&gt;Another problem was the amount of unique books that I parsed from 16 categories. That amount surprised even me: &lt;strong&gt;117,000 unique books&lt;/strong&gt; and &lt;strong&gt;63,000 unique foreign authors&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;So before I found out the exact amount of unique books, I wrote a script based on the &lt;code&gt;deep-translator&lt;/code&gt; library (Google Translator). My first few requests were done without a rate limit, so my IP got blocked. After that, I added &lt;code&gt;time.sleep(2)&lt;/code&gt;. Even after adding a dictionary for saving unique authors and reducing the number of requests, the execution time was too long - like 8 hours or more.&lt;/p&gt;

&lt;p&gt;After that, I found an interesting library called &lt;code&gt;argostranslate&lt;/code&gt;. The main difference is that it’s an &lt;strong&gt;offline library&lt;/strong&gt;, and it works entirely on laptop resources. The process of translating took only &lt;strong&gt;1h 30 min to 2 hours&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Though someone forgot about saving! For Parquet files, Python needs the &lt;code&gt;pyarrow&lt;/code&gt; library. So I installed &lt;code&gt;pyarrow&lt;/code&gt; and will start the translation process on Monday.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Okay, now about some plans. Of course, for book analysis, the next step after translation is working with an API. Now I’m thinking about a few variants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Google Books API:&lt;/strong&gt; With an ordinary free account, I have only 1,000 requests per day. If I connect a payment account, I’ll get 50,000 requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Library API:&lt;/strong&gt; Free, but this API doesn’t have much info about Ukrainian publishers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the best option could be Google Books API. But that’s for next week! &lt;/p&gt;

&lt;p&gt;Now it's the weekend, so I can rest, play games, and watch something interesting. Speaking of watching, first I’m going to watch some Django tutorials (because in the future I’ll definitely need it) and maybe something else.&lt;/p&gt;

&lt;p&gt;Have a great weekend!&lt;br&gt;
&lt;em&gt;#python, #dataanalysis, #learning, #buildinpublic&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>learning</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Building a Chess AI &amp; Book Scraper - Part 5: Memory-Efficient JSON Loading &amp; Switching to a 4/3 Work Balance</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Thu, 23 Jul 2026 09:40:32 +0000</pubDate>
      <link>https://dev.to/eren_poger/building-a-chess-ai-book-scraper-part-5-memory-efficient-json-loading-switching-to-a-43-24no</link>
      <guid>https://dev.to/eren_poger/building-a-chess-ai-book-scraper-part-5-memory-efficient-json-loading-switching-to-a-43-24no</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"Even when your scripts are doing the heavy lifting automatically, watching them run while trying to maintain a 6/1 schedule will burn you out."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey guys ;)&lt;/p&gt;

&lt;p&gt;I’m posting a day later than usual today (Thursday instead of Wednesday), and honestly, that’s part of the update. Over the past few weeks, I’ve been living in a 6 days on, 1 day off routine. When that single day off finally arrived, I felt an overwhelming pressure to cram in every missed habit, chore, and task. Instead of resting, I ended up feeling completely drained.&lt;/p&gt;

&lt;p&gt;So, I’m changing my framework: I’m moving to a &lt;strong&gt;4/3 schedule&lt;/strong&gt; (4 days of intense focus, 3 days of genuine rest and recovery).&lt;/p&gt;

&lt;p&gt;Here is what’s happening with my hardware, my data pipelines, and my mental bandwidth!&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory-Efficient Data Ingestion in Pandas
&lt;/h2&gt;

&lt;p&gt;On Tuesday, I officially wrapped up the main scraping phase for the Ukrainian book market. I finally ran &lt;code&gt;git push&lt;/code&gt; on the repository and started building the processing pipeline in Pandas.&lt;/p&gt;

&lt;p&gt;Instead of calling &lt;code&gt;pd.read_json()&lt;/code&gt; directly, I wrote a custom loader function using &lt;code&gt;with open()&lt;/code&gt; to stream the JSON dictionary before passing it to &lt;code&gt;pd.DataFrame()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd92rcn07ptp20rqf9jwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd92rcn07ptp20rqf9jwe.png" alt="Function to convert Json to Pandas DataFrame" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not just use &lt;code&gt;pd.read_json()&lt;/code&gt;? 🤔
&lt;/h3&gt;

&lt;p&gt;Standard &lt;code&gt;read_json()&lt;/code&gt; can be a memory hog because Pandas attempts to infer structures and allocate buffer space for the entire file at once. While my raw file is currently around 50MB, this approach guarantees that as the dataset grows (after API enrichment), my machine won't choke on RAM limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Case of the Missing 400 Pages
&lt;/h2&gt;

&lt;p&gt;While testing the loader function and inspecting the dataset using &lt;code&gt;.head()&lt;/code&gt;, I noticed a weird anomaly: the top entries were all children's literature (which is Category #2 in my setup).&lt;/p&gt;

&lt;p&gt;Somewhere during the long automated execution, the first 400+ pages of the Fiction category evaporated! 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzzqzorgttwlf5mhli4xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzzqzorgttwlf5mhli4xc.png" alt="Missing category" width="800" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here is my immediate technical checklist for today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Re-scrape the missing Fiction category pages.&lt;/li&gt;
&lt;li&gt;[ ] Run a full deduplication pass across all 16 categories.&lt;/li&gt;
&lt;li&gt;[ ] Integrate the Google Books API to pull missing metadata (publication years, page counts, sub-genres).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ♟️ Chess RL Engine: Extending the Self-Play Deadline
&lt;/h2&gt;

&lt;p&gt;Because I’m adjusting my schedule to a 4/3 split, I am officially extending my self-play generation deadline for the chess project.&lt;/p&gt;

&lt;p&gt;People often think: &lt;em&gt;"Why are you tired? You're just sitting there watching a script scrape pages or a chess engine play itself."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s true that the CPU does the calculation, but managing routines, tracking habits and debugging background crashes. Extending the deadline gives me the breathing room to build a high-quality dataset without turning my personal projects into a stressful second job.&lt;/p&gt;

&lt;p&gt;Here is my screenshots of daily habits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsip8vbcwbfko4iygij99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsip8vbcwbfko4iygij99.png" alt="Monday habits" width="736" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4lutlvagn4r0w576l0y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4lutlvagn4r0w576l0y1.png" alt="Tuesday habits" width="659" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fss2tqw4pqph3an95qn3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fss2tqw4pqph3an95qn3r.png" alt="Wednesday habits" width="659" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm501tz77jaojpnjqcl8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm501tz77jaojpnjqcl8f.png" alt="Thursday habits" width="659" height="594"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Let's Chat!
&lt;/h2&gt;

&lt;p&gt;How do you manage your schedule when building side projects? Have you ever had to restructure your work/rest ratio to prevent burnout? Let’s chat in the comments! 👇&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#pandas&lt;/code&gt; &lt;code&gt;#mentalhealth&lt;/code&gt; &lt;code&gt;#datascience&lt;/code&gt; &lt;code&gt;#showdev&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Chess Engine &amp; Book Scraper - Part 4: Capping the Catalog &amp; 4,500+ Games Cooked</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 18 Jul 2026 15:45:38 +0000</pubDate>
      <link>https://dev.to/eren_poger/building-a-chess-engine-book-scraper-part-4-capping-the-catalog-4500-games-cooked-462k</link>
      <guid>https://dev.to/eren_poger/building-a-chess-engine-book-scraper-part-4-capping-the-catalog-4500-games-cooked-462k</guid>
      <description>&lt;h2&gt;
  
  
  "The data pipeline is perfectly stable, the automation is running smoothly, but I'm not pushing these changes to GitHub yet." 😅
&lt;/h2&gt;

&lt;p&gt;Hey guys ;)&lt;br&gt;&lt;br&gt;
I’m a bit tired after this week's data marathon, so I'll let the numbers and the architecture do the talking today. &lt;/p&gt;

&lt;p&gt;Over the last seven days, my workstation has been split into two universes: generating high-quality training datasets for my chess AI on a strict daily quota, and building a scalable market analysis tool for the book industry. &lt;/p&gt;

&lt;p&gt;Here is the weekly recap, some crucial optimizations, and the roadmap ahead.&lt;/p&gt;


&lt;h3&gt;
  
  
  ♟️ Chess Database: 4,500+ Games Cooked
&lt;/h3&gt;

&lt;p&gt;The automated parallel self-play setup I described in my previous post is performing beautifully. My hardware survived the daily quota of 716 games per day. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Weekly Total:&lt;/strong&gt; 6 * 716 games + 260 baseline test games = 4,556 games successfully generated and saved into the local database. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dataset is expanding exactly as planned, laying down a bulletproof foundation before I open the Jira backlog for the Reinforcement Learning model training phase.&lt;/p&gt;


&lt;h3&gt;
  
  
  📖 The Book Scraper: Smart Capping &amp;amp; Strategy
&lt;/h3&gt;

&lt;p&gt;While the chess engine runs its daily simulations, I’ve been heavily focusing on my new target platform: &lt;strong&gt;Yakaboo&lt;/strong&gt; (the largest book platform in Ukraine). The platform contains &lt;strong&gt;16 distinct main categories&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Initially, I went deep and scraped over 410 pages for the Fiction category alone. But looking at the data, I realized I needed to optimize my traversal mechanics. I decided to introduce a strict limit: &lt;strong&gt;a maximum of 200 pages per category.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Why cap the dataset at 200 pages? 🤔
&lt;/h4&gt;

&lt;p&gt;Online retail storefronts naturally sort their catalog by popularity, search volume, and sales velocity on the initial pages. The deeper you go into page 300+, the higher the density of out-of-print titles, placeholders, or niche products. Scraping thousands of deep pages won't help us identify active consumer trends-it just adds unnecessary noise and processing bloat to the database.&lt;/p&gt;

&lt;p&gt;Here is a sneak peek of how the raw item structure looks right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {
        "title": "Generations in the World of Work",
        "author": "Корі Сіміллер",
        "price": "2947 грн",
        "rating": "0",
        "reviews": "0",
        "status": "Фізична книга",
        "href": "https://www.yakaboo.ua/ua/generations-in-the-world-of-work.html"
    },
    {
        "title": "Paralegal Career For Dummies",
        "author": "Ліза Зіммер Хатч",
        "price": "1832 грн",
        "rating": "0",
        "reviews": "0",
        "status": "Фізична книга",
        "href": "https://www.yakaboo.ua/ua/paralegal-career-for-dummies-3295617.html"
    },
    {
        "title": "Performance Appraisals &amp;amp; Phrases For Dummies",
        "author": "Кен Ллойд",
        "price": "1513 грн",
        "rating": "0",
        "reviews": "0",
        "status": "Фізична книга",
        "href": "https://www.yakaboo.ua/ua/performance-appraisals-phrases-for-dummies.html"
    },
    {
        "title": "A Pluralistic Approach to Leadership: Interdisciplinary Perspectives",
        "author": "Натан В. Хартер",
        "price": "3982 грн",
        "rating": "0",
        "reviews": "0",
        "status": "Фізична книга",
        "href": "https://www.yakaboo.ua/ua/a-pluralistic-approach-to-leadership-interdisciplinary-perspectives.html"
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course some screenshots&lt;/p&gt;

&lt;p&gt;It's a chess database fragment. Yep some games is short, and some is more then 100 moves.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3i0rq4m3qewxeoi1qp2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3i0rq4m3qewxeoi1qp2g.png" alt="Chess database fragment" width="799" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I parsed Yakaboo for 3 days and already parsed almost 23MB of data.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fil3pkbgqp3jbxyyqy9sg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fil3pkbgqp3jbxyyqy9sg.png" alt="Size of parsed data" width="768" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My laptop straggling to parse data while I'm writing posts.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23duyqb7y05cynmwiysm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23duyqb7y05cynmwiysm.png" alt="Btop when I run my parser script" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And what I see as a progress. Of course I could just open Chrome and watch how pages scrolling.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwe9754arnvfcpzo34qn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwe9754arnvfcpzo34qn.png" alt="Console logs with parsing progress" width="799" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Chess Engine in Python - Part 3: Letting the DB Cook &amp; Launching a New Book Market Scraper</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Thu, 16 Jul 2026 11:29:29 +0000</pubDate>
      <link>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-3-letting-the-db-cook-launching-a-new-book-market-5e42</link>
      <guid>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-3-letting-the-db-cook-launching-a-new-book-market-5e42</guid>
      <description>&lt;h2&gt;
  
  
  "What do you do when your laptop is too weak to train a neural network and run game simulations at the same time? I'm letting the database cook, and starting a new small project."
&lt;/h2&gt;

&lt;p&gt;Hey guys ;)&lt;br&gt;&lt;br&gt;
Sorry for being a bit late with this update, but I promise I have some very exciting news. &lt;/p&gt;

&lt;p&gt;Right now, my main Chess Project is entering a transition phase. To train a proper Reinforcement Learning model, I need a massive, high-quality dataset of self-play games. But running heavy simulations and coding at the same time is a heavy burden for my hardware. &lt;/p&gt;

&lt;p&gt;So, I made a strategic decision: &lt;strong&gt;take a 2-week development break to generate the database, while kicking off a brand new Data Science project on the side.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is what's happening on both fronts!&lt;/p&gt;




&lt;h3&gt;
  
  
  ♟️ Chess Project: The Daily Generation Quota
&lt;/h3&gt;

&lt;p&gt;To keep my laptop healthy while still building a solid dataset, I’ve set up a strict automated daily schedule. From now until &lt;strong&gt;July 27th&lt;/strong&gt;, I am running my parallelized self-play script with a specific daily quota:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;716 games per day&lt;/strong&gt; in total (distributed as 358 games per active CPU core).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The depth distribution:&lt;/strong&gt; 300 games at depth 2, 55 games at depth 3, and 3 games at depth 4.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even this "limited" run takes about &lt;strong&gt;2 hours and 30 minutes&lt;/strong&gt; every single day. Once the database reaches its target size on July 27th, I will officially close the generation phase and jump straight into the model training backlog, which is already fully planned and ready in my Jira!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6f99mewc1ufii45el7b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6f99mewc1ufii45el7b9.png" alt="Screenshot of Jira Board for RL sprint" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📖 New Project: Analyzing the Book Industry
&lt;/h3&gt;

&lt;p&gt;While the chess script is running in the background, I decided to tackle a question that has been on my mind for a while: &lt;strong&gt;Are people actually reading fewer books nowadays?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To answer this with cold, hard data, I decided to build a custom web scraper to gather, clean, and analyze retail book market trends. &lt;/p&gt;

&lt;h4&gt;
  
  
  The Pivot: From Amazon to Yakaboo
&lt;/h4&gt;

&lt;p&gt;Initially, my first target was &lt;strong&gt;Amazon&lt;/strong&gt;. However, I quickly ran into two major roadblocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Aggressive Anti-Bot Protection:&lt;/strong&gt; Amazon does &lt;em&gt;not&lt;/em&gt; like scrapers, making automated extraction highly inefficient without expensive proxy setups. Yep I parsed some book data from Amazon via undetected-chromedriver, but there is second problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Limitations:&lt;/strong&gt; The accessible, free-tier product details were too sparse for the deep analysis I wanted to conduct. To compare on Yakaboo there is a books cards with title, author, rating and count of reviews, of course price. So for my analysis I only need to get amount of pages(for example via GoogleBooks API or just right from Yakaboo details page) and interest in certain book(I'm planning to get it via Google Trends)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, I pivoted! My new target is &lt;strong&gt;Yakaboo&lt;/strong&gt;, the largest online book platform in Ukraine. It has incredibly rich metadata regarding which is exactly what I need. And of course their web source code is pretty easier to read.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe8i2ozi0hlflnokxd6yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe8i2ozi0hlflnokxd6yd.png" alt="Yakaboo fiction page" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m currently writing the automation scripts combining &lt;strong&gt;Selenium&lt;/strong&gt; and &lt;strong&gt;BeautifulSoup&lt;/strong&gt; to handle paginated lists and extract the raw dataset.&lt;/p&gt;




&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;For the next two weeks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My CPU will be sweating to generate those 716 chess games daily.&lt;/li&gt;
&lt;li&gt;I will be finalizing the Yakaboo parser, cleaning the scraped book data, and preparing my first analytical dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’m super excited to share actual data insights about what people are reading (and buying) in one of the next post!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you ever had to pause a heavy computational task to prevent your machine from melting? What’s your go-to tool for bypassing anti-bot systems? Let’s share some stories in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#datascience&lt;/code&gt; &lt;code&gt;#webscraping&lt;/code&gt; &lt;code&gt;#chess&lt;/code&gt; &lt;code&gt;#showdev&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Chess Engine in Python - Part 2: Achieving 100% Correctness &amp; Setting Up Multicore Self-Play</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 11 Jul 2026 18:18:27 +0000</pubDate>
      <link>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-2-achieving-100-correctness-setting-up-multicore-4bo1</link>
      <guid>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-2-achieving-100-correctness-setting-up-multicore-4bo1</guid>
      <description>&lt;h2&gt;
  
  
  It’s PEAK.
&lt;/h2&gt;

&lt;p&gt;Hey guys ;)&lt;/p&gt;

&lt;p&gt;Since my last post, I’ve done a massive amount of under-the-hood work. Honestly, writing the code this time felt surprisingly smooth, but the results are a huge milestone for the project: I built a performance/correctness tester, implemented a parallelized self-play system using multiple CPU cores, and hooked it up to a database. &lt;/p&gt;

&lt;p&gt;We are officially at the doorstep of Reinforcement Learning. Let’s break down how it all came together.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Heartbreak (and Triumph) of Perft Testing
&lt;/h3&gt;

&lt;p&gt;In my previous update, I mentioned that I was moving toward &lt;strong&gt;Perft (Performance Test)&lt;/strong&gt; to verify the engine's correctness. A Perft test recursively walks the move tree to a specific depth, counts all total leaf nodes, and compares that number to standard chess data. &lt;/p&gt;

&lt;p&gt;When I ran my first test to depth 3 and 4, my numbers were lower than the official benchmarks on &lt;code&gt;chessprogramming.org&lt;/code&gt;. I'm not going to lie - I was really sad. After days of wrestling with bitwise masks, realizing your engine is missing thousands of moves feels brutal. &lt;/p&gt;

&lt;p&gt;But instead of giving up, I used the test to isolate the discrepancies. It turned out the missing paths weren't deep architectural flaws, but rather sneaky edge cases in pawn logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The En Passant Ghost:&lt;/strong&gt; A bug where specific en passant capture rights weren't being cleared or generated correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out of Index Crash:&lt;/strong&gt; A rare boundary issue when pawns reached the absolute edges of the board during specific masking checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those pawn bugs were squashed, I ran the numbers again. &lt;strong&gt;Success!&lt;/strong&gt; The engine officially proves its 100% mathematical correctness up to depth 4 across multiple complex custom test positions. &lt;/p&gt;




&lt;h3&gt;
  
  
  Multicore Self-Play &amp;amp; Epsilon-Greedy Exploration
&lt;/h3&gt;

&lt;p&gt;With a bulletproof bitboard engine, the next challenge was generating data for our future Reinforcement Learning (RL) agent. To train a model, you need games—thousands of them. &lt;/p&gt;

&lt;p&gt;To achieve this, I implemented a &lt;strong&gt;Self-Play system&lt;/strong&gt;. Here is how the architecture works right now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Epsilon-Greedy Minimax:&lt;/strong&gt; I modified my classic Minimax algorithm into an &lt;em&gt;epsilon-greedy&lt;/em&gt; version. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opening Imitation:&lt;/strong&gt; To prevent the engine from playing the exact same game over and over, the first 4 moves of every match are completely random. This effectively imitates traditional chess openings and forces the engine to explore a massive variety of board states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multicore Scaling:&lt;/strong&gt; Running games sequentially takes ages. I utilized Python's multiprocessing features to distribute the self-play simulation across multiple CPU cores simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Storage:&lt;/strong&gt; Every single generated game, position, evaluated score, and move state is now being piped and safely recorded into a local database. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Next Stop: Reinforcement Learning
&lt;/h3&gt;

&lt;p&gt;The infrastructure is ready. The engine is fast, verified as correct, scales across my CPU, and logs clean training data directly to a DB. &lt;/p&gt;

&lt;p&gt;The next station is implementing and training the actual RL models. This is where the real experiment begins.&lt;/p&gt;




&lt;p&gt;&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#chess&lt;/code&gt; &lt;code&gt;#performance&lt;/code&gt; &lt;code&gt;#multiprocessing&lt;/code&gt; &lt;code&gt;#showdev&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Chess Engine in Python - Part 1: The Immortal King &amp; Bitboard Grid Synchronization</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:40:12 +0000</pubDate>
      <link>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-1-the-immortal-king-bitboard-grid-synchronization-1ong</link>
      <guid>https://dev.to/eren_poger/building-a-chess-engine-in-python-part-1-the-immortal-king-bitboard-grid-synchronization-1ong</guid>
      <description>&lt;h2&gt;
  
  
  "I spent days writing logic masks, only to realize my Chess King became an immortal god who could capture the opposing King."
&lt;/h2&gt;

&lt;p&gt;Connecting a classic &lt;code&gt;8x8&lt;/code&gt; Pygame matrix UI to a new, high-performance bitboard engine felt like a great idea until the synchronization bugs started crawling out. Over the last few days, I've been hunting down some of the most hilarious and stubborn logical errors I've encountered so far. &lt;/p&gt;

&lt;p&gt;Let's dive into what went wrong and how I fixed it.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Mirror Dimension (&lt;code&gt;[row]&lt;/code&gt; vs &lt;code&gt;[7 - row]&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;After building the adapter bridge, the standard pieces moved fine, but two visual elements broke entirely: &lt;strong&gt;move history highlights&lt;/strong&gt; and &lt;strong&gt;available move hint dots&lt;/strong&gt;. They were rendering perfectly on the wrong side of the board.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Cause:&lt;/strong&gt; A classic coordinate mapping issue. While matrices count rows from top to bottom (0 to 7), bitboards index squares from bottom to top. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; The engine was fetching the raw matrix row. Adjusting the coordinate transformation to &lt;code&gt;[7 - row]&lt;/code&gt; instantly brought both worlds into perfect harmony.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Absolute Monarch Bug (King vs. King)
&lt;/h3&gt;

&lt;p&gt;The funniest logical flaw happened during checkmate testing. The King under attack refused to lose. He would block the enemy slider's ray of attack with his own body, consider the squares behind him "safe," and happily wander around until he was literally pinned to the edge of the board. Even worse: kings could stand on adjacent squares and &lt;em&gt;capture each other&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Cause:&lt;/strong&gt; The King was accidentally included in the blocking occupancy masks while generating specific check masks. He was effectively shading himself from the attack rays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Excluded the King's square from the ray-blocking lookup so that attacks correctly pass through his position during validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-2074877950629953802-990" src="https://platform.twitter.com/embed/Tweet.html?id=2074877950629953802"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2074877950629953802-990');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2074877950629953802&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Missing Check Restrictions (Code Refactor)
&lt;/h3&gt;

&lt;p&gt;Another major flaw allowed other pieces (like knights, queens, and bishops) to make random moves even when the King was actively under check. The &lt;code&gt;check_mask&lt;/code&gt; generation logic was breaking down when dealing with sliders.&lt;/p&gt;

&lt;p&gt;Here is how the calculation evolved:&lt;/p&gt;

&lt;h4&gt;
  
  
  How it looked before:
&lt;/h4&gt;

&lt;h1&gt;
  
  
  The mask didn't differentiate piece types, leading to broken lookup tables
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elif count == 1:
    self.in_check_flag = True
    checker_square = check_ers.bit_length() - 1
    checker_bit = 1 &amp;lt;&amp;lt; checker_square
    check_mask = ray_from_king &amp;amp; ray_from_checker | checker_bit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How it looks now (Working Version):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elif count == 1:
    self.in_check_flag = True
    checker_square = check_ers.bit_length() - 1
    checker_bit = 1 &amp;lt;&amp;lt; checker_square
    if checker_bit &amp;amp; (enemy_knight | enemy_pawns):
        check_mask = checker_bit
    else:
        # Properly determining slider type and intersection masks
        is_rook_type = ((checker_bit &amp;amp; enemy_rooks) or (checker_bit &amp;amp; enemy_queen)) and ((king_square // 8 == checker_square // 8) or (king_square % 8 == checker_square % 8))
        if is_rook_type:
            ray_from_king = self.attack_tables.get_rook_attacks(king_square, all_occ)
            ray_from_checker = self.attack_tables.get_rook_attacks(checker_square, all_occ)
        else:
            ray_from_king = self.attack_tables.get_bishop_attacks(king_square, all_occ)
            ray_from_checker = self.attack_tables.get_bishop_attacks(checker_square, all_occ)
        check_mask = ray_from_king &amp;amp; ray_from_checker | checker_bit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By explicitly checking if the attacking piece is a knight/pawn versus a sliding piece (rook/bishop/queen) and calculating the intersecting ray paths properly, all non-evasion moves are now correctly blocked during a check state.&lt;/p&gt;

&lt;p&gt;Minor Fixes &amp;amp; Quality of Life&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pawn Promotion Crash: The matrix engine identified objects as strings ("w_pawn"), while the bitboard engine uses character notation ("P", "Q", "b"). Added a quick mapper to prevent token crashes during promotion.&lt;/li&gt;
&lt;li&gt;    Draw States: Implemented proper engine detection for Threefold Repetition and Insufficient Material (e.g., King vs King + Knight) to ensure matches can actually end legally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's Next? (Proof of Correctness)&lt;/p&gt;

&lt;p&gt;Now that the visual bugs are squashed, I need to make sure the math is 100% accurate. My next milestones in Jira are setting up a Perft (Performance Test) function. It will recursively walk the move tree to a specific depth, count the total leaf nodes, and cross-reference them with official chess move datasets to catch any hidden logical edge cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1b7a5efvdwc5blik2gy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1b7a5efvdwc5blik2gy8.png" alt="Tasks in Jira" width="577" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever had a bug where your game pieces completely ignored the laws of physics or rules? Let’s talk about your favorite debugging horror stories in the comments!&lt;/p&gt;

&lt;h1&gt;
  
  
  python #chess #debugging #showdev
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Chess Project X is Working Again... But With Some Hilarious Bugs!</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 04 Jul 2026 13:27:18 +0000</pubDate>
      <link>https://dev.to/eren_poger/chess-project-x-is-working-again-but-with-some-hilarious-bugs-31i7</link>
      <guid>https://dev.to/eren_poger/chess-project-x-is-working-again-but-with-some-hilarious-bugs-31i7</guid>
      <description>&lt;h2&gt;
  
  
  Hey guys ;)
&lt;/h2&gt;

&lt;p&gt;I’m so glad that from this milestone forward, I can finally share not just lines of code, but actual gameplay footage and visual progress. But let’s take it one step at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewriting the Core &amp;amp; Fighting IDE Autocomplete
&lt;/h3&gt;

&lt;p&gt;After finishing the bitboard engine backend, I duplicated my core architecture files to keep a backup. I started by rewriting the main controller file responsible for game logic and move generation. &lt;br&gt;
Right away, I hit my first roadblock: the new bitboard engine lacked game state tracking variables, so I had to refactor and add state arguments directly to the &lt;code&gt;BitboardEngine&lt;/code&gt; class. &lt;br&gt;
Once that was done, I moved on to the renderer. After adapting all the UI rendering logic to read bitmasks instead of a matrix grid, I hit "Run" with high hopes.&lt;/p&gt;

&lt;p&gt;Guess what? Instead of a beautiful chess UI, I was greeted by a wall of terminal errors. 😅 &lt;br&gt;
One of the funniest bugs was caused by my IDE's autocomplete. It generated &lt;code&gt;move.Move()&lt;/code&gt; even though the &lt;code&gt;Move&lt;/code&gt; class was located in the exact same file. I didn't even notice it until python crashed during execution. &lt;/p&gt;
&lt;h3&gt;
  
  
  The Invasion of the White Pawns &amp;amp; Regicide
&lt;/h3&gt;

&lt;p&gt;After about 30 minutes of polishing micro-bugs, the Pygame window finally opened! I clicked the "Play" button, fixed a few more runtime errors, and could finally move pieces around. &lt;/p&gt;

&lt;p&gt;However, the game state is currently a beautiful mess:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Pawn Uprising:&lt;/strong&gt; Due to a string formatting bug in the asset loader, &lt;em&gt;every single piece on the board rendered as a white pawn&lt;/em&gt;. Funnily enough, they still moved according to their actual hidden piece types (e.g., a "pawn" sliding across the board like a rook).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;King-on-King Violence:&lt;/strong&gt; I forgot to properly include the king in certain restriction masks. As a result, the King could happily walk into check, stand directly next to the opposing king, and even &lt;em&gt;capture&lt;/em&gt; the enemy king straight off the board. Talk about aggressive diplomacy.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;I also need to tweak the promotion logic because there’s currently an issue with argument parsing between the frontend and the new backend. &lt;br&gt;
On Monday, I'll begin a massive bug-hunting session to stabilize special moves (en passant, castling, promotions) and ensure total state synchronization. Once the board state is bulletproof, the environment will be fully ready for Reinforcement Learning training.&lt;br&gt;
I've attached a video of this chaotic gameplay below. I hope you find it entertaining!&lt;/p&gt;



&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-2073396164523626861-786" src="https://platform.twitter.com/embed/Tweet.html?id=2073396164523626861"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2073396164523626861-786');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2073396164523626861&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What’s the funniest visual bug you’ve ever encountered during a major code refactor?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0lu67ovr2vl7hn7g07x7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0lu67ovr2vl7hn7g07x7.jpg" alt=" " width="800" height="434"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnekzh4zpg0owfleympw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnekzh4zpg0owfleympw.jpg" alt=" " width="799" height="432"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2tyj21ttsk8u5s09s9k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2tyj21ttsk8u5s09s9k.jpg" alt=" " width="799" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#pygame&lt;/code&gt; &lt;code&gt;#gamedev&lt;/code&gt; &lt;code&gt;#debugging&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ended Bitboard engine, what's next?</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Tue, 30 Jun 2026 21:16:27 +0000</pubDate>
      <link>https://dev.to/eren_poger/ended-bitboard-engine-whats-next-3dmp</link>
      <guid>https://dev.to/eren_poger/ended-bitboard-engine-whats-next-3dmp</guid>
      <description>&lt;h2&gt;
  
  
  Hey guys ;)
&lt;/h2&gt;

&lt;p&gt;Today I finally finished my bitboard engine! Let me take you through its architecture, and then we'll explore the next steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Architecture
&lt;/h3&gt;

&lt;p&gt;All the engine logic lives in &lt;code&gt;bitboard_engine.py&lt;/code&gt;, which contains the &lt;code&gt;Move&lt;/code&gt; and &lt;code&gt;BitboardEngine&lt;/code&gt; classes. However, for everything to sync up, we rely on a few other modules: &lt;code&gt;board.py&lt;/code&gt; (with the &lt;code&gt;Board&lt;/code&gt; class), &lt;code&gt;move_gen.py&lt;/code&gt; (with &lt;code&gt;AttackTables&lt;/code&gt;), and &lt;code&gt;bitboard_utils.py&lt;/code&gt; (housing &lt;code&gt;BitboardConstants&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here is a breakdown of what each component does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BitboardConstants:&lt;/strong&gt; Contains static methods for fundamental bitwise operations like setting, popping, or getting a bit (&lt;code&gt;set_bit&lt;/code&gt;, &lt;code&gt;pop_bit&lt;/code&gt;, &lt;code&gt;get_bit&lt;/code&gt;), along with constant masks for the board edges (like the A/H files or ranks 1/8).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Board:&lt;/strong&gt; Implements the state of all pieces on the board, handles the initial setup, and tracks tracking piece movements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AttackTables:&lt;/strong&gt; Pre-computes static attack tables for knights and kings, and builds sliding attack tables for rooks, bishops, and queens using &lt;strong&gt;magic numbers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move:&lt;/strong&gt; Describes individual moves, storing data like &lt;code&gt;piece_moved&lt;/code&gt;, &lt;code&gt;piece_captured&lt;/code&gt;, and flags for special moves (&lt;code&gt;is_enpassant&lt;/code&gt;, &lt;code&gt;is_castling&lt;/code&gt;, &lt;code&gt;is_promotion&lt;/code&gt;). It also handles dictionaries for correct chess notation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Heart of the Engine
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BitboardEngine&lt;/code&gt; is where all these components unite. The core functionality is split into two types of functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Move Generation:&lt;/strong&gt; The &lt;code&gt;get_valid_moves&lt;/code&gt; function generates legal moves using complex bitwise masking (check masks, pin masks, enemy attack masks, and edge masks). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management:&lt;/strong&gt; Functions responsible for tracking the game state (like checkmate or draws) and preparing data for the future RL model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Honestly, this was one of the hardest parts of the project due to the sheer amount of math and theory involved, but the performance payoff compared to the matrix engine will be massive.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;Next, I’m going to write two "bridge" functions to map bitboard indices back to the matrix format for the UI. After that, it’s time for rigorous testing. To be honest, I won't rest easy until I test it thoroughly—especially regarding special moves and check detection states. &lt;br&gt;
Once the tests pass, the Reinforcement Learning phase officially begins! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If you have any questions or suggestions about magic numbers or bitboard optimization, let's chat in the comments below!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9a9729ikmd1hjuj83vjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9a9729ikmd1hjuj83vjr.png" alt=" " width="800" height="483"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgzg3jakw9am30752wmt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgzg3jakw9am30752wmt.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffigok5lxouajv8e2avvp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffigok5lxouajv8e2avvp.png" alt=" " width="799" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ymjikv8ve1f39g1qfgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ymjikv8ve1f39g1qfgy.png" alt=" " width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#chess&lt;/code&gt; &lt;code&gt;#bitboards&lt;/code&gt; &lt;code&gt;#showdev&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My first post</title>
      <dc:creator>Nicholas</dc:creator>
      <pubDate>Sat, 27 Jun 2026 19:42:20 +0000</pubDate>
      <link>https://dev.to/eren_poger/my-first-post-4hdl</link>
      <guid>https://dev.to/eren_poger/my-first-post-4hdl</guid>
      <description>&lt;h2&gt;
  
  
  Hey everyone, it’s my first post here ;)
&lt;/h2&gt;

&lt;p&gt;Today I want to show you the project I’m currently working on. It’s &lt;strong&gt;Chess Project X&lt;/strong&gt; (available as &lt;code&gt;chess-project&lt;/code&gt; on GitHub). The development is divided into two main milestones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Milestone 1: The Matrix Engine &amp;amp; UI
&lt;/h3&gt;

&lt;p&gt;The first step was creating a basic chess engine from scratch along with a graphical interface. I built the initial core using matrix logic in pure Python and used the &lt;strong&gt;Pygame&lt;/strong&gt; library for the UI. It includes all the neat features I personally enjoy on Chess.com: analysis arrows, highlighted squares, visual move hints, and even the native sound effects. &lt;br&gt;
For the AI at this stage, I implemented a classic &lt;strong&gt;Minimax algorithm with Alpha-Beta pruning&lt;/strong&gt; running at a maximum depth of 4.&lt;br&gt;
Check video of gameplay on X:&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-2046898283264495656-673" src="https://platform.twitter.com/embed/Tweet.html?id=2046898283264495656"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2046898283264495656-673');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2046898283264495656&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Milestone 2: Transitioning to Bitboards &amp;amp; RL
&lt;/h3&gt;

&lt;p&gt;The ultimate goal is to implement a Reinforcement Learning (RL) agent. Doing this on top of a matrix-based engine would be heavily bottlenecked by performance, so I decided to rewrite the entire engine using &lt;strong&gt;bitboards&lt;/strong&gt;. &lt;br&gt;
&lt;strong&gt;Current state of the rewrite:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implemented a new &lt;code&gt;Board&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Wrote basic bitwise utility functions (&lt;code&gt;set_bit&lt;/code&gt;, &lt;code&gt;get_bit&lt;/code&gt;, &lt;code&gt;pop_bit&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Generated pre-computed attack masks for every piece type.
For the last two days, I’ve been heavily focused on the move validation logic. There are two ways to find legal moves:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Matrix Way:&lt;/strong&gt; Execute &lt;code&gt;make_move&lt;/code&gt;, check if the king is under attack, and then &lt;code&gt;undo_move&lt;/code&gt;. It's easy to code but much slower.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Bitboard Way:&lt;/strong&gt; Generate all possible pin and check masks via bitwise operations. It's significantly harder to implement, but the performance payoff is massive.
Right now, I've finished the logic for the king and knights. Next up: rooks, bishops, queens, and pawns. Once done, I’ll hook it up to the Pygame UI, run tests, and finally start training the RL model!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;I hope you found this breakdown interesting! If you have any questions, tech suggestions, or ideas about bitboards, feel free to drop them in the comments below. See you in the next update!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhk9ocppmvlg6tric6wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhk9ocppmvlg6tric6wg.png" alt=" " width="800" height="205"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6s5b9fhmwp0aexldfxc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6s5b9fhmwp0aexldfxc7.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F881m8pnf1z6qxre1yrxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F881m8pnf1z6qxre1yrxe.png" alt=" " width="799" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#chess&lt;/code&gt; &lt;code&gt;#reinforcementlearning&lt;/code&gt; &lt;code&gt;#bitboards&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
