<?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: Altuğ GÖKOĞLU</title>
    <description>The latest articles on DEV Community by Altuğ GÖKOĞLU (@altug_gokoglu).</description>
    <link>https://dev.to/altug_gokoglu</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1656789%2Fc252f354-1938-4214-aac3-a876edc4a3c1.jpeg</url>
      <title>DEV Community: Altuğ GÖKOĞLU</title>
      <link>https://dev.to/altug_gokoglu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/altug_gokoglu"/>
    <language>en</language>
    <item>
      <title>Working with API Rate Limits: Lessons from Developer Mistakes</title>
      <dc:creator>Altuğ GÖKOĞLU</dc:creator>
      <pubDate>Tue, 13 May 2025 12:27:31 +0000</pubDate>
      <link>https://dev.to/altug_gokoglu/working-with-api-rate-limits-lessons-from-developer-mistakes-11en</link>
      <guid>https://dev.to/altug_gokoglu/working-with-api-rate-limits-lessons-from-developer-mistakes-11en</guid>
      <description>&lt;p&gt;Hello Dev.to readers! This time, we decided to adopt a slightly more serious tone and tackle a serious topic. In our &lt;a href="https://github.com/altuix/new-gen-atari" rel="noopener noreferrer"&gt;New Gen Atari project&lt;/a&gt;, we hit rate limits hard. After that mess, we finally turned to the documentation. To keep you from falling into the same trap, I wanted to write a piece with above-average seriousness.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are API Rate Limits?
&lt;/h2&gt;

&lt;p&gt;API rate limits cap the number of requests you can make to an API in a given timeframe (e.g., 100 requests per minute). Exceeding these limits results in rejected requests, disrupting your application. They’re standard across platforms, whether free or paid tiers.&lt;/p&gt;

&lt;p&gt;Limits serve three key purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Protect servers from malicious request floods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Management&lt;/strong&gt;: Ensure fair access for all users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Control&lt;/strong&gt;: Prevent excessive server resource consumption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignoring these limits can stall projects, but with proper planning, you can work within them effectively.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Developer Mistakes
&lt;/h2&gt;

&lt;p&gt;Developers often stumble when dealing with rate limits due to these pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skipping Documentation&lt;/strong&gt;: Failing to check an API’s limits (e.g., request quotas or time windows) leads to unexpected roadblocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uncontrolled Requests&lt;/strong&gt;: Sending requests relentlessly, especially in loops, quickly exhausts limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Alternatives&lt;/strong&gt;: Not having a fallback plan when limits are hit can halt a project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes are common in projects using free tiers or requiring high request volumes. Poor planning can lead to hours of debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Security Role of Rate Limits
&lt;/h2&gt;

&lt;p&gt;Rate limits are more than just restrictions—they’re a security shield:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DDoS Protection&lt;/strong&gt;: They safeguard servers from overwhelming request floods, ensuring service continuity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Security&lt;/strong&gt;: Limits make it harder for malicious actors to scrape sensitive data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fair Access&lt;/strong&gt;: They prevent large players from monopolizing resources, leveling the field for smaller developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hitting a rate limit isn’t just a technical issue; it’s a reminder of security’s importance. Uncontrolled requests can be mistaken for an attack, potentially blocking your access. Mishandling API keys (e.g., exposing them in code) also poses significant security risks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ethical Solutions
&lt;/h2&gt;

&lt;p&gt;To manage rate limits ethically and effectively, consider these approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Study Documentation&lt;/strong&gt;: Understand the API’s limits. Free tiers often have stricter quotas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Delays&lt;/strong&gt;: Space out requests to reduce server load. Simple timing mechanisms in code can help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Retry Strategies&lt;/strong&gt;: Retry failed requests with increasing delays (exponential backoff) to stay within limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Develop Local Alternatives&lt;/strong&gt;: When APIs fall short, use local logic to keep your project running.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps ensure your project stays on track while respecting server constraints.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Risks of Unethical Approaches
&lt;/h2&gt;

&lt;p&gt;Attempting to bypass rate limits through unethical means is tempting but dangerous. Here’s a deeper look at common illegal or unethical tactics, their risks, and why they’re a bad idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple API Keys&lt;/strong&gt;: Using multiple keys to split requests and exceed limits is a common tactic. However, most APIs track usage by IP or account, making this ineffective. Providers like Cloudflare detect such patterns, leading to account suspension or permanent bans. Violating terms of service can also spark legal issues, especially for commercial projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Servers&lt;/strong&gt;: Routing requests through different IPs via proxies tries to mask overuse. Modern APIs use behavioral analysis to spot this, resulting in IP blacklisting and loss of access to the API or related services. Proxies also introduce latency, degrading performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Botnets&lt;/strong&gt;: Malicious actors may use botnets—networks of compromised devices—to flood APIs with requests. This is a cybercrime, carrying severe legal penalties like fines or imprisonment. Even if undetected, it harms the API provider’s infrastructure and other users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fake Accounts&lt;/strong&gt;: Creating multiple accounts to gain extra quotas is another ploy. APIs often link accounts to payment methods or identities, making detection straightforward. Consequences include account termination and loss of all associated data or credits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fallout from these tactics is steep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bans and Blacklists&lt;/strong&gt;: Providers can block your IP, account, or organization, derailing your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal Risks&lt;/strong&gt;: Breaching terms of service can lead to lawsuits, particularly in commercial settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reputation Damage&lt;/strong&gt;: Unethical behavior can tarnish your credibility in the developer community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Failure&lt;/strong&gt;: Time wasted on failed bypass attempts diverts resources from building robust solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ethical development respects API providers and ensures long-term project success. Securely storing API keys (e.g., in &lt;code&gt;.env&lt;/code&gt; files) and adhering to terms of service are critical steps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;API rate limits may seem like obstacles, but they’re manageable with the right approach. Our brief encounter with limits in New Gen Atari taught us the value of planning and respect for constraints. Read API documentation, plan your requests, and stick to ethical solutions. The Dev.to community wants to hear your API stories—share them in the comments!&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Terms Glossary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting&lt;/strong&gt;: A method to restrict the number of API requests within a specific timeframe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DDoS Attack&lt;/strong&gt;: A cyberattack aiming to overwhelm servers with excessive requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential Backoff&lt;/strong&gt;: A strategy to retry failed requests with increasing time intervals.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>api</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Dark Code: DAN Prompts, Jailbreaks, and the Underground World of AI</title>
      <dc:creator>Altuğ GÖKOĞLU</dc:creator>
      <pubDate>Thu, 08 May 2025 09:31:22 +0000</pubDate>
      <link>https://dev.to/altug_gokoglu/a-dark-code-dan-prompts-jailbreaks-and-the-underground-world-of-ai-10f8</link>
      <guid>https://dev.to/altug_gokoglu/a-dark-code-dan-prompts-jailbreaks-and-the-underground-world-of-ai-10f8</guid>
      <description>&lt;p&gt;As AI systems continue to permeate every aspect of our lives, some users aren't satisfied with the rules and limits these models are bound by. For them, AI is not just a helpful assistant—it's a tool that, if pushed, could do much more. This is where “DAN prompts” come into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are DAN Prompts?
&lt;/h2&gt;

&lt;p&gt;“DAN” stands for “Do Anything Now.” These prompts are designed to trick AI models into ignoring their built-in ethical guidelines and restrictions. Essentially, they attempt to make the model adopt a freer, more rebellious persona that can bypass safeguards and provide otherwise restricted or dangerous information.&lt;/p&gt;

&lt;p&gt;Initially developed out of curiosity or for fun, these methods have since become tools for abuse. DAN prompts are now seen as ways to "unlock" an AI’s hidden potential, often to access unethical or illegal information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jailbreaking AI
&lt;/h2&gt;

&lt;p&gt;The concept of “jailbreaking” in AI borrows from the world of smartphones, where users remove restrictions to gain more control over their devices. In the context of AI, jailbreaking refers to bypassing safety mechanisms and content filters.&lt;/p&gt;

&lt;p&gt;Some users employ jailbreak prompts to get responses about illegal activity, identity fraud, or even instructions for harmful actions. Alarmingly, these prompts often work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt Trading on the Dark Web
&lt;/h2&gt;

&lt;p&gt;Jailbreak prompts have become commodities on the darker corners of the internet. What started on open platforms like Reddit and Discord has moved to private forums and the dark web. Effective DAN prompts are sold for profit—and sometimes that profit is enormous. There are even cases of individuals earning over $100,000 per month by selling them.&lt;/p&gt;

&lt;p&gt;This is no longer just curiosity. It’s organized exploitation. Criminals use jailbroken AI to generate fake identities, plan cyberattacks, or mass-produce manipulative content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recent Security Flaws
&lt;/h2&gt;

&lt;p&gt;Recent studies show that some large language models remain highly vulnerable to malicious prompts. For example, DeepSeek, a China-based AI company, failed all 50 harmful prompt tests in a benchmark conducted by researchers. The model offered up instructions on making bioweapons and methods of self-harm.&lt;/p&gt;

&lt;p&gt;This level of vulnerability poses serious risks—not just for individuals, but for public safety on a much larger scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Companies Are Responding
&lt;/h2&gt;

&lt;p&gt;Some AI companies are stepping up their defenses. OpenAI, for instance, uses layered filtering systems and frequently updates safety protocols. Anthropic, another major AI player, has developed what it calls “constitutional AI.” This approach uses classifiers that judge whether the AI’s output aligns with a defined ethical framework, evaluating both the prompt and the AI’s response.&lt;/p&gt;

&lt;p&gt;Still, jailbreak creators are inventive. This is a game of cat and mouse—constantly evolving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Do We Draw the Line?
&lt;/h2&gt;

&lt;p&gt;At the heart of this issue lies a profound ethical question. For some, bypassing AI safeguards is just fun or a challenge. But when these tactics are used for fraud, misinformation, or real-world harm, we’re no longer talking about play—we’re talking about risk.&lt;/p&gt;

&lt;p&gt;Trust in AI depends on its integrity. When people discover it can be manipulated, confidence drops.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should You Do With This Information?
&lt;/h2&gt;

&lt;p&gt;Given what we now know, here are some key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every prompt you enter shapes the system.&lt;/strong&gt; Manipulative inputs can influence future responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the terms of service of the tools you use.&lt;/strong&gt; Using harmful prompts may get your account suspended or banned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t treat AI as a toy.&lt;/strong&gt; It’s a powerful tool—misusing it can put you and others at risk.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Sources and Further Reading:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/research/constitutional-classifiers" rel="noopener noreferrer"&gt;Anthropic's Constitutional Classifiers: Defending against universal jailbreaks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.wsj.com/tech/ai/china-deepseek-ai-dangerous-information-e8eb31a8" rel="noopener noreferrer"&gt;WSJ – DeepSeek Offers Bioweapon, Self-Harm Information&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ft.com/content/cf11ebd8-aa0b-4ed4-945b-a5d4401d186e" rel="noopener noreferrer"&gt;Financial Times – Anthropic makes 'jailbreak' advance to stop AI models producing harmful results&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.businessinsider.com/artificial-intelligence-cybersecurity-large-language-model-threats-solutions-2025-5" rel="noopener noreferrer"&gt;Business Insider – 'It takes a good-guy AI to fight a bad-guy AI'&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@Bhanu_/chatgpts-100k-month-the-underground-prompt-business-7a764a7097de" rel="noopener noreferrer"&gt;Medium – The Underground Prompt Business ($100k/Month Selling DANs)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://abnormalsecurity.com/blog/chatgpt-jailbreak-prompts" rel="noopener noreferrer"&gt;Abnormal Security – How jailbreak prompts are weaponized by bad actors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>programming</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>No-Code Magic with n8n: I Built a Cheeky Telegram News Bot in Hours!</title>
      <dc:creator>Altuğ GÖKOĞLU</dc:creator>
      <pubDate>Tue, 29 Apr 2025 14:09:54 +0000</pubDate>
      <link>https://dev.to/altug_gokoglu/no-code-magic-with-n8n-i-built-a-cheeky-telegram-news-bot-in-hours-1bb2</link>
      <guid>https://dev.to/altug_gokoglu/no-code-magic-with-n8n-i-built-a-cheeky-telegram-news-bot-in-hours-1bb2</guid>
      <description>&lt;p&gt;Hey there, Dev.to community! 👋 I’m Altuğ, and today I’m excited to share a fun project I built using n8n: a Telegram news bot named &lt;strong&gt;Laubali&lt;/strong&gt;. This bot lets you get news summaries on Telegram by simply typing a category (like "/sports"), and it even adds a funny or slightly cheeky comment to each summary! 😄 Let’s dive into why I started this project and how it all came together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s n8n and Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;If you haven’t heard of n8n yet, you’re in for a treat! 🎉 n8n is an open-source automation tool that lets you connect apps, APIs, and services with a visual, no-code interface. Think of it as a magical bridge that ties your favorite tools together—like Telegram, NewsAPI, or even Slack—without writing a single line of code (unless you want to!). With 86k stars on GitHub, it’s a community favorite! 🌟 You can use n8n to automate repetitive tasks, build bots (like Laubali!), send notifications, sync data between apps, or even create complex workflows. It’s perfect for developers who want to save time and have fun while doing it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Did I Start This Project?
&lt;/h2&gt;

&lt;p&gt;Let’s be honest—there are a million ways to get things done these days. As developers, keeping up with new tech and adapting is crucial. Some tools make our lives so much easier, even if they take away a bit of the “challenge” vibe. But at the end of the day, we all need to finish our tasks quickly and get paid, right? That’s why I decided to explore n8n, a powerful automation tool.&lt;/p&gt;

&lt;p&gt;My motivation was a bit more personal: I was tired of spending hours browsing news sites or endlessly scrolling on Twitter (or X) for updates. I thought, “Why not build a simple news catcher?” And by doing it through Telegram, I could get quick summaries and get on with my day! That’s how Laubali came to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose Telegram?
&lt;/h2&gt;

&lt;p&gt;The biggest reason I chose Telegram is that its APIs are completely free. Instead of building a web app, I found it much easier to just open Telegram, type a message (like "/sports"), and kick off the workflow. I usually read news when I’m out and about, not at my computer. Sending a quick message from my phone to get summaries felt like the perfect solution.&lt;/p&gt;

&lt;p&gt;Plus, Telegram has some awesome features! 🚀 It’s fast, secure, and offers cross-platform support—so it works on your phone, tablet, or computer. Also, Telegram’s bot API is incredibly flexible. You can create a bot with BotFather in minutes and integrate it with n8n. For users, it’s a big win: just type a message and get news summaries instantly, right from your phone or any device!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fun Side of Laubali: Cheeky Comments
&lt;/h2&gt;

&lt;p&gt;One of Laubali’s standout features is the funny and slightly cheeky comments it adds to each news summary. Wondering why I added this? Well, it’s a bit of a social critique. 😏 In my country, even during the toughest times, some news anchors make the most casual and tactless remarks—it’s almost absurd. So, I decided to poke fun at that by naming my bot “Laubali” (which means “tactless” in Turkish for this case) and adding a witty comment to every summary. This feature really spices up the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching Mechanism: Why and How?
&lt;/h2&gt;

&lt;p&gt;While designing Laubali, performance and cost management were key for me. n8n is free, but the &lt;strong&gt;AI Agent&lt;/strong&gt; we use in this project (to summarize news and add comments) isn’t. Many of you reading this might be drawn to a “free” setup, but resource management matters! That’s why I decided to add a caching mechanism.&lt;/p&gt;

&lt;p&gt;The cache ensures that when the same category (like "/sports") is requested repeatedly, we don’t keep hitting NewsAPI and the AI Agent. Instead, we pull the data from the cache. This reduces API call costs and speeds up responses for users. For the first iteration, this solution works perfectly, but I’m sure some of you will add more advanced caching solutions—like using Redis or Supabase. I kept it simple for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Challenges and What I Learned
&lt;/h2&gt;

&lt;p&gt;For someone familiar with n8n, this project would probably take 2-3 hours to complete. But for me, the most challenging part was digging into n8n’s nodes and features by reading the documentation. n8n’s docs are pretty comprehensive, but understanding how each node works took some time. The second biggest challenge was figuring out how to implement the caching mechanism using global data (&lt;code&gt;staticData&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We could’ve used docs, Google Drive, or a database for caching, but I wanted to keep things as simple as possible. The more features you add, the broader the project’s scope becomes. I thought, “If this article gets traction, we can revisit those ideas later.” 😄&lt;/p&gt;

&lt;p&gt;The biggest lesson I learned came after finishing the project. Some people around me said, “We could do this ourselves—what’s the big deal? We’d finish it in no time, no need for tools like this!” But once the project was done, I realized something: Sure, a developer can build this easily. But when it comes to serving hundreds of users quickly at scale, n8n saves a ton of time and gets the job done. I truly appreciated the power of automation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites: Setting Up a Telegram Bot and NewsAPI
&lt;/h2&gt;

&lt;p&gt;Before diving into how Laubali works, let’s go over the technical setup you’ll need to get started. To bring Laubali to life, you’ll need two things: a Telegram bot and an API key from NewsAPI to fetch news. Don’t worry, both steps are super easy! 🚀 Let’s get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Create a Telegram Bot&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We’ll use Telegram’s official bot manager, &lt;strong&gt;BotFather&lt;/strong&gt;, to create a bot. Here’s how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find BotFather&lt;/strong&gt;: Open Telegram, search for &lt;code&gt;@BotFather&lt;/code&gt;, and start a chat with the official BotFather account. (Look for the blue checkmark to avoid impostors! 😄)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a New Bot&lt;/strong&gt;: Send &lt;code&gt;/start&lt;/code&gt;, then send the &lt;code&gt;/newbot&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name Your Bot&lt;/strong&gt;: BotFather will ask for your bot’s name and username. For example:

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;LaubaliNewsBot&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Username: &lt;code&gt;@LaubaliNewsBot&lt;/code&gt; (The username must end with "Bot.")&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get Your Token&lt;/strong&gt;: BotFather will give you an API token, something like: &lt;code&gt;123456:YOUR-TELEGRAM-BOT-TOKEN&lt;/code&gt;. Save this token somewhere safe—you’ll need it to connect your bot to n8n.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Your Bot&lt;/strong&gt;: Go to your bot on Telegram and send &lt;code&gt;/start&lt;/code&gt;. It won’t do much yet, but it’s nice to see it’s ready! 😊&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it! You now have a Telegram bot. We’ll use this token in n8n’s &lt;code&gt;Telegram Trigger&lt;/code&gt; and &lt;code&gt;Telegram&lt;/code&gt; nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Get a NewsAPI Key&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Laubali fetches news from &lt;a href="https://newsapi.org" rel="noopener noreferrer"&gt;NewsAPI&lt;/a&gt;, a great service for pulling news based on categories. To get your API key, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign Up for NewsAPI&lt;/strong&gt;: Head to &lt;a href="https://newsapi.org" rel="noopener noreferrer"&gt;NewsAPI&lt;/a&gt; and click “Get API Key” to create an account. You can sign up with your email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grab Your API Key&lt;/strong&gt;: After signing up, NewsAPI will provide you with an API key, like: &lt;code&gt;YOUR-NEWSAPI-KEY&lt;/code&gt;. Save this key as well.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the API (Optional)&lt;/strong&gt;: You can test the API in your browser with this URL:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   https://newsapi.org/v2/everything?q=sports&amp;amp;pageSize=5&amp;amp;apiKey=[YOUR_API_KEY]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return 5 news articles for the "sports" category. If you see a JSON response with news, you’re good to go!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The free plan of NewsAPI has some limitations (like a daily request limit). That’s why our caching mechanism will come in handy for this project! 😉&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Set Up n8n&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, make sure you have n8n installed. If you’re running n8n locally, you might need a tool like &lt;code&gt;ngrok&lt;/code&gt; to create a webhook URL (since Telegram needs a webhook URL to listen to your bot). If you’re using n8n’s cloud version, you’ll already have a webhook URL. For more details, check out the &lt;a href="https://docs.n8n.io" rel="noopener noreferrer"&gt;n8n documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Laubali Work?
&lt;/h2&gt;

&lt;p&gt;Let’s dive into the technical side of Laubali. When a user types a category (like "/sports"), the bot follows these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capture the Category&lt;/strong&gt;: The &lt;code&gt;Telegram Trigger&lt;/code&gt; node catches the user’s message ("/sports") and the &lt;code&gt;Set Interest&lt;/code&gt; node stores it in the &lt;code&gt;interest&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate the Category&lt;/strong&gt;: The &lt;code&gt;If Valid Interest&lt;/code&gt; node checks if the category is valid (e.g., "/sports", "/technology"). If it’s invalid, the user gets an error message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Cache&lt;/strong&gt;: The &lt;code&gt;Check Static Data&lt;/code&gt; and &lt;code&gt;If Cache False&lt;/code&gt; nodes check if there’s data in the cache and if it’s less than 30 minutes old.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If there’s cached data&lt;/strong&gt;: The &lt;code&gt;Set Cached Summaries&lt;/code&gt; node grabs the summaries, and &lt;code&gt;send cached message&lt;/code&gt; sends them to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If there’s no cached data&lt;/strong&gt;: The workflow continues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetch News&lt;/strong&gt;: The &lt;code&gt;NewsAPI&lt;/code&gt; node pulls 5 news articles for the selected category from &lt;a href="https://newsapi.org" rel="noopener noreferrer"&gt;NewsAPI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarize and Add Comments&lt;/strong&gt;: The &lt;code&gt;Edit Fields&lt;/code&gt; node formats the news, then the &lt;code&gt;AI Agent&lt;/code&gt; node summarizes each article and adds a funny comment. I also added a &lt;code&gt;Simple Memory&lt;/code&gt; to the AI Agent. It’s not actively used right now, but in the future, it’ll help with conversational continuity (e.g., if the user says, “Summarize more sports news,” it can recall the context).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache the Summaries&lt;/strong&gt;: The &lt;code&gt;Set Cache Data&lt;/code&gt; nodes save the summaries to the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send to the User&lt;/strong&gt;: Finally, the &lt;code&gt;Send Message&lt;/code&gt; node sends the summaries to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow runs smoothly and delivers results quickly, with the cache ensuring near-instant responses for repeated requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Use Laubali?
&lt;/h2&gt;

&lt;p&gt;For me, time is precious. I’d rather get daily news summaries quickly without wading through ad-heavy, toxic platforms, so I can focus on my day. When I want in-depth news, I’ll dive into those platforms and do my research. But for a quick daily summary while sipping my coffee, Laubali is the perfect solution! ☕&lt;/p&gt;

&lt;p&gt;Laubali’s best feature, in my opinion, is its simplicity and the ad-free experience it offers. It’s ideal for anyone who wants to stay informed quickly while having a bit of fun along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of the Project and a Gift for You
&lt;/h2&gt;

&lt;p&gt;While working on this project, I realized that n8n is a gateway to a vast world of automation. But I intentionally kept the project’s scope small. I think this article provides enough lessons and examples for anyone looking to learn n8n and see a sample project in action. Laubali will stay active for a few days before I shut it down. But I want everyone to benefit from this project!&lt;/p&gt;

&lt;p&gt;So, I’m sharing the entire workflow in a &lt;code&gt;laubali-workflow.json&lt;/code&gt; file: &lt;a href="https://github.com/altuix/n8n-rpg/blob/aa54283399d7acdc3a2807feb467bb3169b10d09/laubali-n8n-workflow.json" rel="noopener noreferrer"&gt;laubali-n8n-workflow.json&lt;/a&gt;. To use this workflow, copy the JSON content from the link, go to your n8n instance, and import it via the "Import Workflow" option. Make sure to set up your Telegram bot token and NewsAPI key in the respective nodes! You can load this file into your n8n instance to use Laubali as your own bot or build on top of it. Maybe you’ll add a conversational loop, or perhaps a news verification feature—it’s totally up to you! 😄 (Remember the prerequisites, otherwise it WILL NOT WORK!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building Laubali was both a learning experience and a lot of fun. Exploring n8n’s automation capabilities, simplifying my news consumption, and adding a bit of humor with a social critique made this project a joy to work on. I hope this article inspires you to create your own automation projects! What else would you add to Laubali? I’d love to hear your ideas in the comments! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Want to Try This Project
&lt;/h2&gt;

&lt;p&gt;If you’d like to access and use this project, it’s super simple! Just search for &lt;a href="https://t.me/LaubaliBot" rel="noopener noreferrer"&gt;&lt;strong&gt;@LaubaliBot&lt;/strong&gt;&lt;/a&gt; on Telegram and start using it. (I want to make it clear: this bot will never ask you for money or anything like that. There are a lot of fake bots on Telegram, so I’m warning you just in case you come across one!)&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build, Break, Learn: 4 Must-Know Lessons for New Devs</title>
      <dc:creator>Altuğ GÖKOĞLU</dc:creator>
      <pubDate>Thu, 24 Apr 2025 09:01:42 +0000</pubDate>
      <link>https://dev.to/altug_gokoglu/build-break-learn-4-must-know-lessons-for-new-devs-66e</link>
      <guid>https://dev.to/altug_gokoglu/build-break-learn-4-must-know-lessons-for-new-devs-66e</guid>
      <description>&lt;p&gt;Hey there, dev.to community!&lt;/p&gt;

&lt;p&gt;I’ve been coding for years, but some days, I still feel like a junior developer. Because, well, I’m human! That feeling keeps my early days vivid in my memory. Back then, my excitement to build things was off the charts, but I realize now I burned that energy recklessly. Neighborhood pressure, greed, feeling like time was infinite, chasing every idea… I’ve been through it all. In this post, I’m sharing four lessons I learned from my mistakes with junior developers (and those who still feel like juniors at heart). I bet you’ll see a bit of yourself in here!&lt;/p&gt;

&lt;h2&gt;
  
  
  Neighborhood Pressure: “What Will They Say?”
&lt;/h2&gt;

&lt;p&gt;Building something on your own is awesome, but sometimes our brains play tricks on us. As a junior, I’d think, “If I put this code on GitHub, everyone will laugh!” or “This project isn’t cool enough.” As humans, we’re always chasing the best, sometimes even perfection. But is that drive coming from within, or is it the “neighborhood pressure” pushing us? We create imaginary scenarios in our heads, like “They’ll say this, they’ll say that.”&lt;/p&gt;

&lt;p&gt;Then it hit me: Even if you create the universe, someone will say, “This isn’t it.” Don’t get mad at those who leave harsh comments. They’re the fuel that unintentionally pushes you forward! Sure, you might have supportive people around, but at the end of the day, you’re alone in your head. Scary? Nope, it’s liberating! Because no one’s dissecting your code. Harsh feedback is just an obstacle that moves you forward. Remember: &lt;em&gt;The obstacle is the path itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Don’t fear criticism—embrace it. Forge your own path and let go of the “what will they say” anxiety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Greed and Time: Tame Your Ambition, Master Your Time
&lt;/h2&gt;

&lt;p&gt;If you’re a developer, learning is a lifelong sport. You &lt;em&gt;should&lt;/em&gt; fear becoming outdated. But trying to learn every new framework, every library? That’s greed. I used to jump on everything: ‘Unity? Let’s make a game! Ruby? Why not! Blockchain? I’m in! Embedded systems? Sure!’ The result? Half-baked knowledge and unused projects. What’s the point of learning if you don’t apply it? Chasing the “Everyone’s learning this!” hype on social media might make you look shiny, but it won’t cut it in the industry.&lt;/p&gt;

&lt;p&gt;Time is the biggest casualty of this greed. As a junior, I acted like time was infinite. I’d wrestle with a bug for hours, only to crash from exhaustion. Then I got to know myself: My focus drifts every 25 minutes. Is that a flaw? Nope! Now I get up for water every 25 minutes. That’s my style. I know folks who code uninterrupted for 5 hours—awesome! But everyone’s got their own rhythm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Rein in your learning ambition and focus on what your project needs. Find your work rhythm: Need a break every 30 minutes? Great, plan around it. Own your time—it doesn’t come back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing the Line: Avoid the Graveyard of Half-Finished Projects
&lt;/h2&gt;

&lt;p&gt;This is one of my biggest traps. I’m still taming the beast inside me! When building something, keeping it within realistic boundaries is crucial. As a junior, I tried to turn every project into a “world-changer.” For a recipe app, I thought, “Why not add 3D WebGL plate animations?” The result? Half-finished projects and “I’ll come back to this” lies.&lt;/p&gt;

&lt;p&gt;Recently, I built a tic-tac-toe game. Two-player mode, AI opponent mode… It was done. But I didn’t stop. “Let’s make a killer homepage!” I said, and spent 3 days messing with WebGL for a next-gen Atari vibe. Then, “Why not add more AI modes?” Thankfully, I stopped there. I used that time to write this post instead. I built something &lt;em&gt;and&lt;/em&gt; learned my limits. Would 10 extra AI modes have been cool? Sure, but who cares? The product was ready, and no one might’ve even noticed! 😄 What mattered was that I finished it.&lt;/p&gt;

&lt;p&gt;Another trap I hit? Chasing every “brilliant idea.” Well-meaning folks come up with, “Dude, let’s build this app!” It’s usually just a sentence. Unresearched, untested. You take it on, but it derails you from your main path. Once, I got hooked on such an idea and spent weeks researching. Turns out? Someone else had already built it. &lt;em&gt;A road paved with good intentions, you know the rest…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Set your project’s scope upfront. Don’t chase every idea—finish your own work first. Instead of leaving a trail of half-done projects, bask in the confidence of a completed product.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI and Unfamiliar Code: The Cooked Fish Trap
&lt;/h2&gt;

&lt;p&gt;The way we learn new things has changed a lot. As juniors, we’d hunt for mentors, take unpaid gigs, and learn by falling and getting back up. Now? We’ve got AI! It’s a fantastic helper but also a dangerous trap. AI can hand you a “cooked fish,” but the real goal is learning to fish.&lt;/p&gt;

&lt;p&gt;For my tic-tac-toe project, I asked AI to mentor me. I used this prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Act as an experienced software developer mentor. Guide me through the project step by step without writing code directly; instead, explain which technologies to use, the steps to follow, and what I should research. For each step, clarify why you chose this approach and suggest reliable resources to explore. Use a friendly, motivating tone that fits wild and fun projects, but encourage me to learn independently. Ensure the project is feasible, technically achievable, and has a clear outcome to avoid incomplete or impractical work. If I get stuck, offer a specific example or hint, but only after I’ve had a chance to research and try on my own.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This turned AI into a guide. It suggested technologies and pointed me to research resources. But was it always right? Nope! Sometimes I ran into outdated info. For example, it recommended a library, but a quick Google search led me to a newer alternative.&lt;/p&gt;

&lt;p&gt;The biggest trap? Copy-pasting code you don’t understand. Imagine a developer stuck on a project. They grab a code snippet from AI or a forum, paste it, and it works! Awesome, right? Not so fast. That code might leave a security hole in database queries because they didn’t get what it does. LLMs can mess up. They might pull code from an error-riddled StackOverflow post. This stalls your learning and can land you in hot water. If you’re lucky, you just waste time. If not? &lt;em&gt;Security experts throw a party.&lt;/em&gt; 😄&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Use AI as a mentor, but question its suggestions. Before copying code, read it and Google what it does. Don’t risk your learning process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing: Keep Building, But Stay Human
&lt;/h2&gt;

&lt;p&gt;I’ve worked with code gods, even folks who’d rip the Backspace key off their keyboard, claiming, “I don’t make mistakes, so why bother?” But the greatest virtue is owning your flaws. Growth comes from facing those gaps.&lt;/p&gt;

&lt;p&gt;Maybe you weren’t born with god-given talent. That means you and I have to work the hardest. But this journey doesn’t have to be painful. Keep building, keep growing. Your projects, yourself, your quality of life, your hobbies, your relationships—improve them all. Developers can be introverted beasts, but don’t lose your humanity along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go build something now!&lt;/strong&gt; Start a small project today. Finish it. Share it. Even if no one looks, &lt;em&gt;you did it.&lt;/em&gt; That’s worth everything.&lt;/p&gt;




</description>
      <category>beginners</category>
      <category>productivity</category>
      <category>motivation</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Not Overengineer? Tic-Tac-Toe’s AI Chaos</title>
      <dc:creator>Altuğ GÖKOĞLU</dc:creator>
      <pubDate>Tue, 15 Apr 2025 09:48:37 +0000</pubDate>
      <link>https://dev.to/altug_gokoglu/why-not-overengineer-tic-tac-toes-ai-chaos-1icm</link>
      <guid>https://dev.to/altug_gokoglu/why-not-overengineer-tic-tac-toes-ai-chaos-1icm</guid>
      <description>&lt;p&gt;Picture this: it’s a quiet night, I’m nursing a coffee, and my code editor’s mocking me with its blank stare. Then, a wild idea strikes—why not take the simplest game ever, Tic-Tac-Toe, and juice it up with AI? Was it overkill? Absolutely. A sandbox for learning? You bet. We wrestled with quirky models, battled APIs, and laughed through the chaos. The result? A working game and a treasure chest of lessons. Curious? The code’s all here: &lt;a href="https://github.com/altuix/new-gen-atari" rel="noopener noreferrer"&gt;github.com/altuix/new-gen-atari&lt;/a&gt;. This project was about exploring how a simple game could be transformed with AI, uncovering the quirks of modern tech stacks, and learning through trial and error. Let’s dive in! &lt;a href="https://new-gen-atari.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Who’s This For?
&lt;/h3&gt;

&lt;p&gt;This is for tinkerers—coders who see a basic project and think, “How can I make this &lt;em&gt;wild&lt;/em&gt;?” If you’re new to React, itching to mess with AI, or just love a challenge, this is your jam. Seasoned devs, you might chuckle at our detours, but stick around—there’s something here for you too. Whether you’re looking to dive into AI integration, master API handling, or enjoy a quirky coding adventure, this project offers hands-on lessons for all skill levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the Point?
&lt;/h3&gt;

&lt;p&gt;Why overcomplicate a game kids master in minutes? Because it’s a perfect playground to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Experiment with AI integration.&lt;/li&gt;
&lt;li&gt;Test modern frameworks like Next.js.&lt;/li&gt;
&lt;li&gt;Learn from epic fails and small wins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what we uncovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crafting Effective Prompts&lt;/strong&gt;: Clear and specific prompts are crucial for meaningful AI responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Integration Challenges&lt;/strong&gt;: Backend-frontend glue requires careful coordination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling Errors&lt;/strong&gt;: Rate limits and debugging taught us patience and planning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparing AI Models&lt;/strong&gt;: Free tools teased; paid ones delivered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;Our toolkit was a mix of modern and mad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt;: Chosen for seamless frontend and backend handling via API Routes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt;: Enabled a responsive UI with efficient state management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face&lt;/strong&gt;: Provided a free playground for models like DistilBERT, GPT-2, and Mistral-7B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI&lt;/strong&gt;: Delivered GPT-4o-mini for advanced reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt;: The backbone tying it all together.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: The Humble Beginning
&lt;/h3&gt;

&lt;p&gt;Every epic starts small. Mine? A two-player Tic-Tac-Toe in Next.js and React. Goal: a 3x3 grid where X and O take turns. In &lt;code&gt;gameBoard.tsx&lt;/code&gt;, I set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Cell&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/cell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GameBoard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setGameState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;xTurn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setXTurn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cellAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cellId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cellId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;xTurn&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;o&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;setGameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setXTurn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;xTurn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid grid-cols-3 grid-rows-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Render cells */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cells got clickable in &lt;code&gt;Cell.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cellId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cellAction&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex bg-white box-border size-20 border-2 border-black font-black text-5xl uppercase items-center justify-center text-black cursor-pointer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cellAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cellId&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;winCases.ts&lt;/code&gt; sealed the deal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkWinner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;winCombinations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;combo&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;winCombinations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;combo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked like a charm—too easy, though. Time to spice it up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: AI Enters the Chat
&lt;/h3&gt;

&lt;p&gt;Next thought: “What if AI played O?” Next.js API Routes kept it all in-house. In &lt;code&gt;opponent.ts&lt;/code&gt;, I hooked up an API call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getOpponentMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/OpenAi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;getOpponentMove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next.js API Routes allowed us to manage backend logic within the same project, streamlining communication between the frontend and AI services.&lt;/p&gt;

&lt;p&gt;Then, in &lt;code&gt;gameBoard.tsx&lt;/code&gt;, I triggered AI moves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opponentAiMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOpponentMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;cellAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Opponent move failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;xTurn&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;vsAI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;opponentAiMove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;xTurn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was hyped—until I realized the AI part was the real puzzle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Free Models, Big Flops
&lt;/h3&gt;

&lt;p&gt;“Let’s try free stuff first,” I thought, diving into Hugging Face with &lt;code&gt;huggingface/route.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promptTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HuggingFaceInference&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;distilbert-base-uncased-finetuned-sst-2-english&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HUGGINGFACE_API_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAIMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;promptTemplate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isValidMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We tested a few free models from Hugging Face, hoping they’d play Tic-Tac-Toe. Here’s what we tried and what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DistilBERT (distilbert-base-uncased-finetuned-sst-2-english)&lt;/strong&gt;: Prompted with “Pick an empty cell,” it responded with “positive.” Sentiment analysis? Sure. Game moves? Nope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-2&lt;/strong&gt;: Asked to “Choose ‘row_column’,” it gave “3_4” on a 3x3 grid. Out of bounds and out of luck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistral-7B&lt;/strong&gt;: Told to “Pick a spot,” it spat out random gibberish every time. Consistency? Not found.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These models struggled because they’re optimized for tasks like sentiment analysis or text generation, not the structured logic required for a game like Tic-Tac-Toe.&lt;/p&gt;

&lt;p&gt;Hours of tweaking prompts—“Stay in bounds!” “Empty cells only!”—led nowhere. We left &lt;code&gt;promptTemplate&lt;/code&gt; as &lt;code&gt;"hey"&lt;/code&gt;, a sarcastic surrender. The results were hilarious but useless.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why the Flops? A Quick Reflection
&lt;/h4&gt;

&lt;p&gt;Let’s be fair: these models aren’t bad—they’re just not made for this. DistilBERT excels at sentiment analysis, not grid-based logic. GPT-2 generates text, not strategies. Mistral-7B handles general tasks, but Tic-Tac-Toe is a stretch. The problem wasn’t the models—it was me, trying to force them into something weird with vague prompts. My lack of prompt-engineering finesse didn’t help either. They’re great in their own domains; my odd demands were the real flop here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: OpenAI Saves the Day
&lt;/h3&gt;

&lt;p&gt;Done with freebies, we tapped OpenAI’s GPT-4o-mini in &lt;code&gt;open-ai/route.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promptTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invalidMoves&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
You are playing Tic-Tac-Toe as O on a 3x3 grid (rows and columns: 0-2). 
'X' is your opponent, 'O' is you, '0' is empty. 
Your move MUST be an empty cell ('0') with coordinates 'row_column'.
Do NOT suggest occupied cells or invalid coordinates.
Current board: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GAME_STATE&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
Respond with 'row_column' only.
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Previous invalid: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;invalidMoves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAIMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;invalidMoves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invalidMoves&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isValidMove&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;GAME_STATE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;invalidMoves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI move error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It wasn’t flawless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early moves hit occupied cells.&lt;/li&gt;
&lt;li&gt;Costs crept up (pennies, but still).&lt;/li&gt;
&lt;li&gt;A 429 “Too Many Requests” error forced a pay-as-you-go switch. We hit the free tier’s rate limit, so we added card details and switched to a pay-as-you-go plan, which resolved the issue instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once sorted, GPT-4o-mini played like a pro—reasoning, adapting, winning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: The Road Not Taken
&lt;/h3&gt;

&lt;p&gt;Here’s the twist: we could’ve used Minimax, a slick algorithm for perfect Tic-Tac-Toe moves. Minimax evaluates every possible move to guarantee the best outcome, making it ideal for games like Tic-Tac-Toe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isMaximizing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;checkWinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;o&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMaximizing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kc"&gt;Infinity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;o&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minimax would’ve crushed it in a day. Why skip it? This was about exploration, not efficiency. The scenic route taught us more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Game On!
&lt;/h3&gt;

&lt;p&gt;Tying it together in &lt;code&gt;gameBoard.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opponentAiMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOpponentMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;cellAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Opponent move failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked! Not flawless—AI lagged sometimes—but playable. We tested the game with friends, tweaking the AI’s response time to make it feel more natural, and celebrated every hard-fought win. Victory tasted sweet after the struggle.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We Learned
&lt;/h3&gt;

&lt;p&gt;This wild ride left us wiser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompts Matter&lt;/strong&gt;: Clear, specific prompts are crucial for getting meaningful AI responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs Bite Back&lt;/strong&gt;: Rate limits and errors require careful planning and debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free vs. Paid&lt;/strong&gt;: Free models are great for experimenting, but paid ones offer reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overkill Wins&lt;/strong&gt;: Tackling complex challenges builds skills you won’t gain from simple tasks.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;We built an AI Tic-Tac-Toe—was it nuts? Yes. Awesome? Absolutely. I’m hooked—next up, maybe an over-the-top Pong or a neural-net Snake. Imagine an AI-driven Pong where the paddle predicts your moves or a Snake game that learns your playstyle—any takers for those? Got ideas? Hit the comments—let’s overcomplicate something else together!&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/" rel="noopener noreferrer"&gt;https://nextjs.org/docs/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;https://react.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/docs" rel="noopener noreferrer"&gt;https://huggingface.co/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english" rel="noopener noreferrer"&gt;https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/openai-community/gpt2" rel="noopener noreferrer"&gt;https://huggingface.co/openai-community/gpt2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mistral.ai/news/announcing-mistral-7b" rel="noopener noreferrer"&gt;https://mistral.ai/news/announcing-mistral-7b&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/docs/api-reference" rel="noopener noreferrer"&gt;https://platform.openai.com/docs/api-reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/finding-optimal-move-in-tic-tac-toe-using-minimax-algorithm-in-game-theory/" rel="noopener noreferrer"&gt;https://www.geeksforgeeks.org/finding-optimal-move-in-tic-tac-toe-using-minimax-algorithm-in-game-theory/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
