<?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>Forem: Eugene Cheah</title>
    <description>The latest articles on Forem by Eugene Cheah (@picocreator).</description>
    <link>https://forem.com/picocreator</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%2F56734%2F7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg</url>
      <title>Forem: Eugene Cheah</title>
      <link>https://forem.com/picocreator</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/picocreator"/>
    <language>en</language>
    <item>
      <title>How I Became a 100x Developer - As a Junior</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Thu, 05 Jan 2023 03:30:05 +0000</pubDate>
      <link>https://forem.com/picocreator/how-i-became-a-100x-developer-as-a-junior-87f</link>
      <guid>https://forem.com/picocreator/how-i-became-a-100x-developer-as-a-junior-87f</guid>
      <description>&lt;p&gt;The idea of a 10x developer, or a rockstar developer, being better than every other developer at everything, is a myth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Instead, you should strive to be a 100x developer in very specific situations, a 10x developer in some, and a 1x developer in as many as possible. It's important to be aware of the areas you excel at and the areas that you may need to work on.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unless you work for a large tech company, it's likely you'll be wearing multiple hats. On a daily basis, you'll need to be able to switch between front-end, back-end, and language-specific development, such as Java or TypeScript. This is due to the ever-changing nature of our industry and the sheer number of ways to achieve the same tasks - &lt;a href="https://en.wikibooks.org/wiki/Computer_Programming/Hello_world" rel="noopener noreferrer"&gt;like printing “Hello World” in over 400 different programming languages.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5vqz7nyc3891ua9okwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5vqz7nyc3891ua9okwp.png" alt="XKCD Comic, on standards" width="500" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's impossible to be good at everything, but it is possible to be great at something.&lt;/p&gt;

&lt;p&gt;To elaborate, I'd like to share a story from over a decade ago which was a life-changing moment that had a huge impact on my career.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Became a 100x Developer - as a Junior
&lt;/h2&gt;

&lt;p&gt;I worked with a large MNC and was part of a team that was struggling to keep up with demand.&lt;/p&gt;

&lt;p&gt;They had built a custom Java web application that thousands of employees were using daily, but as the number of records going in and out of the SQL servers scaled up. Things were slowing down. They had already upgraded the SQL server to its limits and needed new solutions. An obvious one was to use memcache to cache some of the results.&lt;/p&gt;

&lt;p&gt;Unfortunately, memcache had been banned due to some security incidents in the past and the lack of HA. The managers had been trying to get approval for memcache for over a year but failed. Meanwhile, the devs fought an uphill battle as every 10% improvement was negated by double that in user growth.&lt;/p&gt;

&lt;p&gt;This is where I came in, and joined.&lt;/p&gt;

&lt;p&gt;I had no idea about their past 1 year of struggle and had been brought on board as a junior developer to help make some minor improvements to the UI, while the seniors focused on performance.&lt;/p&gt;

&lt;p&gt;However because, I was very annoyed by the slow performance of the dev server. And I had been playing with Hazelcast - a brand new Java-based server clustering and caching library.&lt;/p&gt;

&lt;p&gt;I used it as an alternative to memcache. And was able to get it running on the application, which had no special restrictions or special approval requirements, that killed all previous solutions.&lt;/p&gt;

&lt;p&gt;And had a working prototype with a demo ready within the same week. For one page in the platform, the API calls went from more than 10 seconds to under a second. It was a game-changer.&lt;/p&gt;

&lt;p&gt;The whole team jumped on board, and within a month, we had a "clustering solution which is definitely not memcache" in production.&lt;/p&gt;

&lt;p&gt;As my team manager and his boss treated everyone to a banquet, my manager said to me, that when he brought me on board as a smart kid, he had never expected me to be a 10x or 100x programmer who could solve their problems overnight.&lt;/p&gt;

&lt;p&gt;And that statement struck me hard.&lt;/p&gt;

&lt;p&gt;Because I was an imposter – nothing but a lucky imposter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl9uotkcv6rs5za2ap8y.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl9uotkcv6rs5za2ap8y.jpeg" alt="Image of an imposter, in among us the game" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lucky Imposter – Who Was a 0.1x Developer
&lt;/h2&gt;

&lt;p&gt;Technically, I was not a 1x or even a 10x developer. I knew less than my teammates, and I was learning on the job.&lt;/p&gt;

&lt;p&gt;My peers could do the front end better than me and the team knew how to optimize SQL (and taught me, thank you!). My biggest credit was that I was able to do the equivalent of “npm install miracle” and read its manual on how to configure it.&lt;/p&gt;

&lt;p&gt;In hindsight, there were many issues I had dodged by luck.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Politics (which blocked memcache in the first place)&lt;/li&gt;
&lt;li&gt;Use case (we had no write contention safety, meaning if 2 edits happen in the same ms, the cache will be badly updated. Thankfully, this rarely ever happens)&lt;/li&gt;
&lt;li&gt;Stability (v1 of hazelcast crashes like crazy, thankfully the API server was designed to be deployed in HA mode, so this was an annoyance to the infra team at worst)&lt;/li&gt;
&lt;li&gt;Security (Having an open port to all data on the API server in a cluster, was a bad idea on my part, and a very junior mistake until another senior thankfully read the docs on how to secure it)&lt;/li&gt;
&lt;li&gt;Language stack (if the server was in Python, .NET, C# or Ruby, I would have no idea how to even include a local cluster cache)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was lucky to have a handful of one-trick ponies, which made me 100x in the right situations. Which were only possible because of the foundational work put in place by the rest of the team.&lt;/p&gt;

&lt;p&gt;I was also lucky that my managers and bosses allowed me to choose the problems I wanted to fix, as they fast-tracked me, and threw me across multiple teams to "fix their problems". While allowing me to maximise my luck-to-impact ratio and repeat the “miracles”.&lt;/p&gt;

&lt;p&gt;With experience, I also realised the opposite – I am slow at front-end development. This was what I was originally supposed to be doing before I was fast-tracked as a 10x engineer and it made me reflect a lot over the years. As it could have been the path I would have been stuck on.&lt;/p&gt;

&lt;p&gt;It also struck me that in hindsight, having "mastered" hazelcast, and various other technologies. How much it would have failed for other teams, even in the same organisation, were it not for the unique situation, and safeguards, which should have been credited to the rest of the team.&lt;/p&gt;

&lt;p&gt;I struck lucky, as a junior, if anything else.&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;h2&gt;
  
  
  This is why Senior Developers Tend to be 10x Junior Developers
&lt;/h2&gt;

&lt;p&gt;Once we look beyond the generalisations of engineering, it becomes clear that everyone has their skills, which can range from 100x to 0.1x.&lt;/p&gt;

&lt;p&gt;Over the years, as I moved across many teams and projects, I eventually became a “senior” developer. During this time, I realised that seniors generally have more skills, and know what they are good and bad at. They can proactively inform their managers, and prioritise tasks accordingly. This doesn’t mean they know how to do everything, but it means they know what not to do.&lt;/p&gt;

&lt;p&gt;Juniors, on the other hand, struggle with this as they lack the experience to know what they are good at or bad at. There isn’t an easy way to find this out other than to just “try”.&lt;/p&gt;

&lt;p&gt;Once this is understood, it becomes apparent that it’s all about maximising the number of things you know you can do reliably and well. It’s also about maximising your luck factor when it comes to being assigned tasks, and ensuring you’re not stuck with things you’re bad at.&lt;/p&gt;

&lt;p&gt;In a way for juniors, their progress will depend heavily on the alignment of their luck and skillset. Seniors, had more (but not full) control, over how to steer their progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;In this case: Luck isn’t binary. You can increase the number of situations that align with you.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z11r3mnqoasqq64bfyt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z11r3mnqoasqq64bfyt.jpg" alt="Image of a pair of dice, representing luck" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Maximise Your Situation Luck at 100x and 10x
&lt;/h2&gt;

&lt;p&gt;For everyone, but especially for juniors and those just starting out, the best thing to do is to keep trying new things in technology and exploring. This will help you figure out what you’re good at and what you’re bad at.&lt;/p&gt;

&lt;p&gt;For those who have a better idea of what they are good at, the next step is to expand the number of situations where they can perform their best. This involves some practice and research into adjacent technologies to the ones they already know. Once you become great at something, make sure to own it so your managers and bosses know that this is what they should give you. This will increase the chances of hitting those 10x or 100x situations more frequently.&lt;/p&gt;

&lt;p&gt;Find at least one thing you’re good at, even if it’s a very small and narrow thing. Build up from there. (Some notable examples include regex and configuring webpack.)&lt;/p&gt;

&lt;p&gt;For seniors, if you haven’t done so already, start looking beyond the technical aspects of development. This doesn’t mean you have to move into management. But with your unique technical knowledge, you can play a more active role in understanding the user or business use case. This will allow you to optimize with your managers to ensure 10x or 100x success rates for you and your team.&lt;/p&gt;

&lt;p&gt;For those who are job hunting, if you know you’re good at something, even if it’s 10x, do some research and dig deeper into your job hunt. Try to find a well-funded startup or MNC that is facing difficulty in the exact thing you’re good at. While this can be difficult to pull off and does involve some networking, if you place yourself in the right place and time, it will maximize the impact for both you and the company involved.&lt;/p&gt;

&lt;p&gt;These are all things, you can do slowly over time, to improve the luck factor. And deliver that 10x experience more consistently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Special shout out to read @&lt;a href="https://dev.to@swyx"&gt;swyx&lt;/a&gt; article on how to create luck: &lt;a href="https://www.swyx.io/create-luck/" rel="noopener noreferrer"&gt;https://www.swyx.io/create-luck/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqfk90lyqo6hf1jrqr36.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqfk90lyqo6hf1jrqr36.jpg" alt="Team of people, holding lego figs together" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  As a CTO and Team Leader Today, This Still Holds - 10x Is a Myth - Everyone Is Both 100x and 0.1x
&lt;/h2&gt;

&lt;p&gt;Some may think that the CTO of a UI testing tool such as Uilicious.com would be good with front-end technologies. However, this is far from the truth. Our average fresh hire or junior in the company is typically faster than me the CTO when it comes to writing front-end components with modern front-end frameworks such as Vue.js or React.&lt;/p&gt;

&lt;p&gt;This has changed my perspective as a team leader/manager. It showed me how toxic and bad the 10x engineer myth is. It’s an oversimplification of labels and generalization of very unique situations. It’s a toxic misconception that needs to be fixed.&lt;/p&gt;

&lt;p&gt;Almost every startup 10x engineer myth, when looked into deeper, turns out to have an individual with unique knowledge in the right place and time. And was able to avoid the tasks they were 0.1x at.&lt;/p&gt;

&lt;p&gt;In my case, it was focusing on infrastructure over front-end development. Where instead, I have other team members who are 1000x times better at doing frontend development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;This is an important realization as a manager because, for every individual who is 100x at one task, there’s a task that makes them 0.1x as they get stuck in a time sink. It’s about understanding and recognizing what each individual is 100x at, and what they’re 0.1x at.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this sounds very straightforward, this is significantly harder in practice.&lt;/p&gt;

&lt;p&gt;There are many things a team leader or developer will not know till they have tried it before. Or are given the time and opportunity, to hone and master the skillsets involved in those specific situations. There are also many things, especially new technologies, that no one is good at it, and is all about taking risks and a chances.&lt;/p&gt;

&lt;p&gt;It’s the team leader’s job to organize tasks accordingly to best suit everyone’s needs. And if that is not possible, it's also about understanding that even if someone can’t be 100x in your situation now, they can potentially be elsewhere in other teams.&lt;/p&gt;

&lt;p&gt;And is on every individual developer, to figure out what they are good and bad at, and to communicate that to their team accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;So to death with the 10x Engineer Myth, Long live the 100x Engineer in specific situations&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PS: If you have not made any new year's resolutions. Perhaps increasing your luck surface area could be it!&lt;/p&gt;

&lt;p&gt;~ 🖖 live long and prosper&lt;br&gt;&lt;br&gt;
Eugene Cheah: CTO of &lt;a href="http://uilicious.com" rel="noopener noreferrer"&gt;&lt;strong&gt;uilicious.com&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Originally posted on: &lt;a href="https://substack.tech-talk-cto.com/p/dev-to-cto-how-do-i-become-a-10x" rel="noopener noreferrer"&gt;https://substack.tech-talk-cto.com/p/dev-to-cto-how-do-i-become-a-10x&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Image credits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Austin Neill - rockstar image: &lt;a href="https://unsplash.com/photos/sxxnM-tRlVg" rel="noopener noreferrer"&gt;https://unsplash.com/photos/sxxnM-tRlVg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;XKCD - comic on standards: &lt;a href="https://xkcd.com/927/" rel="noopener noreferrer"&gt;https://xkcd.com/927/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CenturiiC - Comic on experienced engineers: &lt;a href="https://twitter.com/CenturiiC/status/1566060367825883136" rel="noopener noreferrer"&gt;https://twitter.com/CenturiiC/status/1566060367825883136&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lucas Santos - Dice picture: &lt;a href="https://unsplash.com/photos/XIIsv6AshJY" rel="noopener noreferrer"&gt;https://unsplash.com/photos/XIIsv6AshJY&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vlad Hilitanu - Lego figs: &lt;a href="https://unsplash.com/photos/1FI2QAYPa-Y" rel="noopener noreferrer"&gt;https://unsplash.com/photos/1FI2QAYPa-Y&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>career</category>
    </item>
    <item>
      <title>VC &amp; Founders Note : ChatGPT / LLM, is a major change moving forward - heres why</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Fri, 16 Dec 2022 03:41:05 +0000</pubDate>
      <link>https://forem.com/picocreator/vc-founders-note-chatgpt-llm-is-a-major-change-moving-forward-heres-why-3gcp</link>
      <guid>https://forem.com/picocreator/vc-founders-note-chatgpt-llm-is-a-major-change-moving-forward-heres-why-3gcp</guid>
      <description>&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's ignore the public hype that is chatGPT for a moment, its ethics or its implications to society (for now), and let's focus on the implications of building an AI based on its research and the tech, for your startup or your portfolio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TLDR 1: Dataset is no longer a hard requirement&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The idea that you need tons of data to make text-based AI useful is now partially outdated&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Even an extremely small dataset can produce valuable results (1 sentence ~ 1GB)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Something which nearly all startups are either sitting on, or is able to produce by hand&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TLDR 2: It has very broad use cases&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;You can think of GPT 3.5 as a remote &amp;amp; paid “college intern on demand”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;You can never trust interns to be 100% right, but they can provide useful value with proper supervision, guidance and planning&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Value that can now grow and scale seamlessly (unlike real human interns)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TLDR 3: Cost and Limitations&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Classic unit economics model for SaaS and online services will be a challenge&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;due to the extremely high running cost of most LLM&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;OpenAI also has a (temporary) effective monopoly, for the infrastructure in this space, and can be both a partner and potential unintentional future competitor at the same time.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you were on holiday, and have no idea what are ChatGPT or LLM (large language models) - you can find the briefing about the technology here:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/picocreator" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F56734%2F7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg" alt="picocreator"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/picocreator/introducing-ai-chatgpt-and-how-it-dreams-in-2022-3bla" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Introducing AI / ChatGPT, and how it dreams in 2022&lt;/h2&gt;
      &lt;h3&gt;Eugene Cheah ・ Dec 6 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#openai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tooling&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;So what do I mean in longer form?&lt;br&gt;&lt;br&gt;
Grab a cup of coffee and settle in for a bit…&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskyq48jteykoqykiw1v6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskyq48jteykoqykiw1v6.jpg" alt="Image of a harddisk drive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Data is no longer a Hard Requirement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Crash Course On Traditional Text-Based AI Data Requirements
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This article is discussing only text-based models and does not apply to image models in many cases (e.g., computer vision).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the past, building an AI model typically involved gathering a large enough dataset for a specific use case. Different AI model training techniques and tools existed to optimize the training process or its computational costs. However, the size and quality of the dataset was still one of the predominant factors in training an AI.&lt;/p&gt;

&lt;p&gt;This would result in a graph something like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9ja2qnb6sal9layzily.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9ja2qnb6sal9layzily.png" alt="Graph showing the practical usefulness of AI, starting at negative, before growing slowly and exponentially at approximately ~500GB in size where it past being "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All numbers are large approximations and will change drastically depending on the use case, AI model, and data quality. The scales on the axes are intentionally vague and inaccurate because they are subjective. For technical numbers, read the papers in the citations for specific use cases.&lt;/p&gt;

&lt;p&gt;The general idea remains, however, where small-scale datasets produce effectively random data before slowly becoming better and finally being somewhat useful at approximately the ~500GB part.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This led to the dataset arms race, occurring in nearly every field of specialization related to AI (especially anything computer vision-related). Which played out across multiple startups from 2009 onwards until today, with a few notable startups being acquired in part for their valuable datasets (and usually talent).&lt;/p&gt;

&lt;p&gt;Because the arms race was constantly escalating, it became increasingly difficult for new startups to compete with their AI models (small datasets) against incumbents (larger datasets).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, at uilicious.com (low-code UI test automation), we used our limited dataset to train an AI model (called TAMI v0.1). We found that it was spewing garbage half the time, leading us to set the model aside and grow the company without AI while building up our dataset.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a very overgeneralised and non-technical fashion, AI trained in this way will be referred to as "Specialized Models" from here onwards.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Start of LLMs or Large Language Models
&lt;/h3&gt;

&lt;p&gt;In the quest for a truly universal or general-purpose AI, especially in the field of human speech interactions (as humans make for the most random of all variables), efforts arose to train a new type of text-based AI for extremely large datasets of public data (think Wikipedia, Quora, StackOverflow, and half of the internet text).&lt;/p&gt;

&lt;p&gt;Because these new models pushed the boundaries of dataset sizes and model size (think brain size), the methods used to build them diverged from specialized models (which tend to have more emphasis on accuracy and efficiency).&lt;/p&gt;

&lt;p&gt;Text AI models trained in this way are now collectively called "Large Language Models" (LLMs).&lt;/p&gt;

&lt;p&gt;The downside of this approach was huge, preventing its early application in many use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Extremely large datasets need to be built and maintained (GPT-3 used 45 TB)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Training costs millions of dollars (GPT-3 cost over $4M to train, GPT-3.5/chatGPT is presumably higher)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited use for specialized use cases due to the lack of private training data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lower accuracy than specialized models in specialized use cases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expensive to run (more on this in limitations later)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;LLM was dominated by Google (who already had the data and had the motive to do so for their AI assistant) and OpenAI initially. Later, Microsoft, Amazon, Apple, Salesforce, Meta, and a few others joined the race.&lt;/p&gt;

&lt;p&gt;However, due to the sheer size and cost to train such a model, it's generally reserved for major tech companies with deep pockets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Though the first few generations of LLMs may have produced disappointing results, as they lost out to nearly every specialized model in every task, things changed as years passed and they scaled up in both dataset size and model size.&lt;/p&gt;

&lt;p&gt;Their benefits became more visible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Facts and accuracy aside, they became really good at talking to humans&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In many (but not all) cases: they are really good at learning new specialized knowledge when given the datasets in an appropriate format (you can't just dump it in)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made drastic changes to the curves:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F796ikl2s2r5wi15l9qt7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F796ikl2s2r5wi15l9qt7.png" alt="Graph showing how AI models trained on top of LLM models, can rapidly gain useful level of efficiency, with very small datasets, growing very rapidly, before hitting a diminishing point of return quickly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also sparked a new wave of AI-backed business models in 2019. Startups could now train new AI models on top of existing LLMs with datasets within their reach, from chatbots to presentation slide generators, code copilots, copywriting and even D&amp;amp;D gamemasters.&lt;/p&gt;

&lt;p&gt;No longer was these AI models in the exclusive domain of large tech companies. For a small fee for training and running the AI on their infrastructure, OpenAI and Google started opening up their models for others to build upon as a means to profit from the LLMs.&lt;/p&gt;

&lt;p&gt;This was beneficial to startups, too, as they no longer needed millions of dollars in R&amp;amp;D investment required to build these large models internally, allowing them to reach the market faster with their proof-of-concepts.&lt;/p&gt;

&lt;p&gt;Data was still, in a way, king. It may have moved the curve to be more accessible, but it still required a team to build up large datasets and tune the model. As a result, while many of the 2019 startups found it significantly easier to build their prototypes, many found it difficult to cross the "useful" threshold, as they needed to scale up their dataset with diminishing returns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is consistent with early internal trials of uilicious.com TAMI AI models - basically it was between interesting and "meh".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But that's when things start to really change in 2022....&lt;/p&gt;

&lt;h3&gt;
  
  
  How Large Language Models are Few-Shot Learners
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fre95wkyhpyatr83bpg84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fre95wkyhpyatr83bpg84.png" alt="Chart showing the level of usefulness for for AI models trained on GPT 3.5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The release of GPT3.5 (or chatGPT as it's known on the internet) last month (December 2022) had huge implications for the way AI models are trained and fine-tuned.&lt;/p&gt;

&lt;p&gt;It has unlocked the possibility of creating usable AI with extremely small datasets - something that most startups have access to or can create by hand. This is a fundamental shift in how we think about AI training.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At uilicious.com, we were stunned when we discovered that a small sample dataset smaller than 1GB, of our larger ~100GB dataset. When converted and optimised with the new training techniques, had exceeded the "useful threshold" - the point at which the AI could be utilised by our users and outperform everything we had previously.&lt;/p&gt;

&lt;p&gt;Subsequent experiments with larger datasets showed diminishing returns. The key takeaway, was how little data was needed to "make a useful product". It took us less time to build a proof-of-concept AI, fine-tuned for our proprietary use case, than it took to write this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using GPT3.5 as a basic building block, it is now possible to build useful AI applications for various use cases without needing a specialised team or individual. Depending on the use case, the dataset can be as small as a single sentence or paragraph, or up to 100MB or 1GB - a size that is achievable for many startups.&lt;/p&gt;

&lt;p&gt;Alternatively, if you can get chatGPT to act in a way that you find useful and of value to your startup. You can build it as a dedicated AI service.&lt;/p&gt;

&lt;p&gt;The drastic reduction in required dataset made it possible for us to build a "useful" AI model with only a small fraction of our full dataset - something that had previously been "useless" or "impossible" at our scale.&lt;/p&gt;

&lt;p&gt;In many ways data is no longer king, integration and finding useful application is the true kingmakers with this new AI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7smo4xgkhaswkdyqxh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7smo4xgkhaswkdyqxh5.png" alt="Example of our new AI assistant converting a prompt to search a cooking recipe"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fus1g0hgnqdif16cvzkmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fus1g0hgnqdif16cvzkmm.png" alt="Fully usable test script, from a single prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The screenshot is a demo of our AI in action, generating full UI testing scripts, from a prompt, as an example integration.&lt;/p&gt;

&lt;p&gt;Something which is now doable in a week, with the right team, and the right data.&lt;/p&gt;

&lt;p&gt;Do reach out to us if you are interested in the future of AI and UI testing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaomgsd37qc3ngrt8dg0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaomgsd37qc3ngrt8dg0.jpg" alt="Picture of a lady using a binoculous"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: The Hunt for Use Cases (and AI Interns) Has Begun
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The intern framework for finding use cases
&lt;/h3&gt;

&lt;p&gt;GPT-3.5 and all other large language models have limits on their accuracy and reliability. This is partly due to their nature of &lt;a href="https://dev.to/picocreator/introducing-ai-chatgpt-and-how-it-dreams-in-2022-3bla"&gt;dreaming out answers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While in theory (yet to be proven), given a large enough specialized dataset, its accuracy can be drastically improved for specialized use cases. However, if you have such a large dataset, chances are you could already build "specialized models".&lt;/p&gt;

&lt;p&gt;Ultimately still, this limit on accuracy and reliability is only a critical issue in sensitive industries (e.g. medicine). For most cases, it is merely a distraction in finding use cases. Once the AI crosses the “good enough” threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A healthier and more realistic approach to the problem is to imagine GPT-3.5 as a remote and paid “smart college intern on demand”.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because realistically, the AI has all the same limitations in such use cases, in addition to being remote and online:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Never trust interns to be 100% right all the time (let alone diagnose medicine)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a workflow that allows the intern to safely provide useful value, with the appropriate supervision, guidance, and planning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For one-day or one-month interns, you would create a formal training plan to quickly get them productive in one hour or one week of training, respectively&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You should be paying your interns; they are not free (both for humans and AI)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only real benefit the AI model has over real-life interns is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;They are not affected by school seasons&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can scale both up and down at any second&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The training is something you can do once per batch and scale up to as many instances&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The downside the AI has over the humans is that they cannot get you coffee in person.&lt;/p&gt;

&lt;p&gt;Once you frame it in these terms, it becomes significantly easier to figure out how to integrate the AI into existing business processes or products.&lt;/p&gt;

&lt;p&gt;Need to generate article ideas? Get a one-day intern. Need to convert some proprietary Java to JavaScript code? Get a one-month intern with some basic training on how to do the process. And so on and so forth - at no point in the process should the human be removed from the loop in supervising and iterating with the intern.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On a slightly more technical level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A one-day intern can be quickly built using prompt engineering; anything you manage to get chatGPT to do fits into this category. The downside is there is a practical limit (about 2000 words) of what you can fit into the training data. The plus side is you can experiment and set this up in seconds, and it's very easy to setup or iterate with chatGPT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A one-month intern (or anything in between) is where you start having a formal training dataset, which they can memorize and learn. They will be able to handle more situations according to the training materials. The downside is you actually need to prepare that training material; however, combined with both techniques, it can get very far.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxd5xz27egdkwucz5ock.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxd5xz27egdkwucz5ock.jpg" alt="Picture of a calculator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Cost and Business Limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cost per Prompt Will Make or Break the Business Model
&lt;/h3&gt;

&lt;p&gt;This is the biggest weakness for this newer approach in AI, which is based on bigger and better models. Unfortunately, there is no free lunch. While it is cheaper and easier to train for specific tasks in terms of dataset size, it is significantly more expensive to run compared to more traditional AI models.&lt;/p&gt;

&lt;p&gt;It is not cheap; the cost per prompt and answer range anywhere between one cent and fifty cents, depending on how much data was needed to train or use in the process. This is drastically higher than a typical API server, which can handle a million requests per dollar.&lt;/p&gt;

&lt;p&gt;To put it in simpler terms, it costs more in server hardware to handle one AI query for one user within a given second than it does to serve a million user requests for a typical medium-sized Shopify website. This is not due to OpenAI or Azure soley trying to make a profit; it is down to the pure server hardware infrastructure required to run such large models.&lt;/p&gt;

&lt;p&gt;As a result, as powerful as it is, incorporating such Large Language Model AI has a hefty price tag and may not be applicable for all use cases due to this limitation alone.&lt;/p&gt;

&lt;p&gt;The end result is that, while many use cases can benefit from the use of such an AI, not all use cases can afford it; and this should be an important consideration for any implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example A: Support Service Chat
&lt;/h4&gt;

&lt;p&gt;For a support service desk, a typical support staff can handle ten customers per hour, with each customer having an average of fifteen back-and-forth prompts. If it is five cents per prompt - this adds up to $7.50 s an hour if an AI was used to mimic a single support staff.&lt;/p&gt;

&lt;p&gt;This is not only cheaper than the typical US call center staff median salary of $15 per hour, it is also much more flexible (no staffing overhead, can scale up and down instantly).&lt;/p&gt;

&lt;p&gt;Likewise, it is also possible to take the same "intern" approach, where these support AI serve only as L1 support, allowing the humans to handle the more complicated cases. In this scenario, it makes sense when done and scaled appropriately on a per-prompt (or per-hour) basis.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example B: A SaaS Service to Draft Emails
&lt;/h4&gt;

&lt;p&gt;The average office worker replies to approximately forty emails per work day, or approximately 880 emails per month. Even at five cents per email, that would be $44 per month on average per user just to handle the email replies.&lt;/p&gt;

&lt;p&gt;What makes it worse is that it's reasonable to expect that, with such a service, an office worker would be able to reply to more emails on average. It would not be unreasonable for the average to double to two thousand emails, or one hundred dollars a month, in pure AI costs alone. In this case, if the SaaS startup were to do a simple, let's say ten-dollar-per-month pricing, they can get into heavy potential losses over time.&lt;/p&gt;

&lt;p&gt;This pricing cost and business model runs counter to the typical, per-user pricing model that is common in SaaS. Which is why it is common for such integrations to have a "credits" system as a means to cap the usage, and a means of billing, of such an AI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is expected that overtime, with better fine-tuning, competition, and cost optimization, the price per prompt can go down. Another notable method is to use the original more expensive AI first at launch, while collecting more data, which is then used to train a more specialized and cheaper model. However, all of these methods go into deep technical details, which may be unique for each use case, and generally require significant time and effort.&lt;/p&gt;

&lt;p&gt;And even then, while it can be a drastic ten times in savings, it is fundamentally more expensive than traditional SaaS API services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  OpenAI Has an Effective Monopoly (For Now)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Prices May Be Stuck Until Competition Arrives
&lt;/h4&gt;

&lt;p&gt;While there are existing open-source Large Language Models, in very frank terms, they are either comparable to GPT2 or somewhere in between that and GPT3.5.&lt;/p&gt;

&lt;p&gt;For some simple cases, once they start building up a reasonable dataset, these smaller (and cheaper) models may be useful to migrate into as a means of cost cutting.&lt;/p&gt;

&lt;p&gt;However, for other complex cases, such a move may not be possible due to the complexity of their AI, giving OpenAI an effective monopoly with no incentive for lower pricing.&lt;/p&gt;

&lt;p&gt;It is believed, however, that over the course of the next one or two years, the open-source community will catch up and, in doing so, perhaps allow prices to improve due to better alternative infrastructure providers. However, because this is an uncertain future, it is worth highlighting.&lt;/p&gt;

&lt;h4&gt;
  
  
  OpenAI Is a Potential Unwilling Competitor for Some Models
&lt;/h4&gt;

&lt;p&gt;While not intentionally, it is critical that startups within this space build feature sets that are defensible beyond their prompt-to-text bots.&lt;/p&gt;

&lt;p&gt;For example, there were several smaller startups who built GPT3-based bots or SaaS around specific prompt-to-text use cases, like name generators or idea generators with a simple interface.&lt;/p&gt;

&lt;p&gt;Literally overnight, with the launch of chatGPT, these small single-use-case text-to-text SaaS were made redundant, as everyday folks can now get the same functionality via chatGPT for free. While it may not have been OpenAI's intention to compete with the very partners that build on them, it may be its inevitable nature to do so, as they keep improving their model and chatGPT. As such, to ensure this does not repeat itself, it is critical for any business model around this technology to figure out what additional value they provide beyond just prompt-to-text, perhaps better user experience or integrations with existing toolings, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lastly: It Ain't Perfect
&lt;/h3&gt;

&lt;p&gt;A reminder of the intern model - do not expect to use this for curing cancer tomorrow. So please do not go stuffing AI into every product and startup on Earth when it makes no benefit to the end user to do so.&lt;/p&gt;




&lt;p&gt;~ Until next time 🖖 live long and prosper&lt;br&gt;&lt;br&gt;
Eugene Cheah : CTO of uilicious.com&lt;br&gt;&lt;br&gt;
This article was originally posted on the authors &lt;a href="https://substack.tech-talk-cto.com/p/vc-and-founders-note-chatgpt-llm" rel="noopener noreferrer"&gt;substack&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvhwur3wv137lrqe9p8y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvhwur3wv137lrqe9p8y.jpg" alt="Picture of sunrise"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes that didn't fit in
&lt;/h2&gt;

&lt;p&gt;In the AI / ML industry, the ability for a LLM to quickly learn new concepts and apply them is quantified and measured by tests called "Zero-Shot", "One-Shot" and "Few-Shot" learnings.&lt;/p&gt;

&lt;p&gt;Generally the better the AI does in these test, the less data you need to train it for your use case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In my opinion: This makes sense in hindsight - who would have thought that a Neural Network modeled after humans, would act like humans. And benefit from the T-Shape education model. Where a large breadth of generalised knowledge, help improve ones ability to learn specialised knowledge in one are of expertise. (this statement is not backed by any data)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;swyx also does an excellent job in trying to consolidate various information, in this rapidly chaotic and growing space (recommended readings)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Day The AGI Was Born: &lt;a href="https://lspace.swyx.io/p/everything-we-know-about-chatgpt" rel="noopener noreferrer"&gt;https://lspace.swyx.io/p/everything-we-know-about-chatgpt&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How Open Source is eating AI: &lt;a href="https://lspace.swyx.io/p/open-source-ai" rel="noopener noreferrer"&gt;https://lspace.swyx.io/p/open-source-ai&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;His live AI notes: &lt;a href="https://github.com/sw-yx/ai-notes/blob/main/TEXT.md#chatgpt" rel="noopener noreferrer"&gt;https://github.com/sw-yx/ai-notes/blob/main/TEXT.md#chatgpt&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently BLOOM is the main opensource contender for GPT3 (not 3.5): &lt;a href="https://towardsdatascience.com/run-bloom-the-largest-open-access-ai-model-on-your-desktop-computer-f48e1e2a9a32" rel="noopener noreferrer"&gt;https://towardsdatascience.com/run-bloom-the-largest-open-access-ai-model-on-your-desktop-computer-f48e1e2a9a32&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other sources and citations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GPT-3 45TB training set: &lt;a href="https://www.springboard.com/blog/data-science/machine-learning-gpt-3-open-ai/" rel="noopener noreferrer"&gt;https://www.springboard.com/blog/data-science/machine-learning-gpt-3-open-ai/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GPT-3 estimated $4 million++ plus training cost: &lt;a href="https://lambdalabs.com/blog/demystifying-gpt-3" rel="noopener noreferrer"&gt;https://lambdalabs.com/blog/demystifying-gpt-3&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bloom AI, an opensource competitor $1.6 million training cost: &lt;a href="https://techcrunch.com/2022/07/12/a-year-in-the-making-bigsciences-ai-language-model-is-finally-available/" rel="noopener noreferrer"&gt;https://techcrunch.com/2022/07/12/a-year-in-the-making-bigsciences-ai-language-model-is-finally-available/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;T-Shaped model of education and work&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://wordspy.com/index.php?word=t-shaped" rel="noopener noreferrer"&gt;https://wordspy.com/index.php?word=t-shaped&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://collegeinfogeek.com/become-t-shaped-person/" rel="noopener noreferrer"&gt;https://collegeinfogeek.com/become-t-shaped-person/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Large language models papers, for zero and few shot learnings&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://arxiv.org/abs/2205.11916" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2205.11916&lt;/a&gt; (zero shot)&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://arxiv.org/abs/2005.14165" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2005.14165&lt;/a&gt; (few shot)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Call center salary rates in US : &lt;a href="https://www.comparably.com/salaries/salaries-for-call-center-agent" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.comparably.com/salaries/salaries-for-call-center-agent" rel="noopener noreferrer"&gt;https://www.comparably.com/salaries/salaries-for-call-center-agent&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Average email sent by an office worker a day: &lt;a href="https://www.templafy.com/blog/how-many-emails-are-sent-every-day-top-email-statistics-your-business-needs-to-know/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.templafy.com/blog/how-many-emails-are-sent-every-day-top-email-statistics-your-business-needs-to-know/" rel="noopener noreferrer"&gt;https://www.templafy.com/blog/how-many-emails-are-sent-every-day-top-email-statistics-your-business-needs-to-know/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;OpenAI announcement of chatGPT / GPT3.5 : &lt;a href="https://openai.com/blog/chatgpt/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://openai.com/blog/chatgpt/" rel="noopener noreferrer"&gt;https://openai.com/blog/chatgpt/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Unsplash photo credits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://unsplash.com/photos/7swaW1bYpWI" rel="noopener noreferrer"&gt;https://unsplash.com/photos/7swaW1bYpWI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://unsplash.com/photos/1iVKwElWrPA" rel="noopener noreferrer"&gt;https://unsplash.com/photos/1iVKwElWrPA&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://unsplash.com/photos/SELXIJwN24s" rel="noopener noreferrer"&gt;https://unsplash.com/photos/SELXIJwN24s&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://unsplash.com/photos/3xwrg7Vv6Ts" rel="noopener noreferrer"&gt;https://unsplash.com/photos/3xwrg7Vv6Ts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://unsplash.com/photos/-G3rw6Y02D0" rel="noopener noreferrer"&gt;https://unsplash.com/photos/-G3rw6Y02D0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>openai</category>
      <category>ai</category>
      <category>tooling</category>
      <category>startup</category>
    </item>
    <item>
      <title>Introducing AI / ChatGPT, and how it dreams in 2022</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 06 Dec 2022 10:02:28 +0000</pubDate>
      <link>https://forem.com/picocreator/introducing-ai-chatgpt-and-how-it-dreams-in-2022-3bla</link>
      <guid>https://forem.com/picocreator/introducing-ai-chatgpt-and-how-it-dreams-in-2022-3bla</guid>
      <description>&lt;p&gt;with large language models (like chatGPT), and AI art generation - everything we know about tech in the next few years, maybe changing drastically&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article focuses more of the current state of AI, on 5th Dec 2022. As a means of briefing others outside this industry, what has been going on recently, and what are its limitations. With a focus on recent developments in chatGPT, over long term speculations.&lt;/p&gt;

&lt;p&gt;Recommended music for the article - GITS Stand Alone Complex: Be Human - &lt;a href="https://music.youtube.com/playlist?list=OLAK5uy_meCQS7-RZ4TX2ku8XkVGxW41J9gkfQTto&amp;amp;feature=share"&gt;@youtube-music&lt;/a&gt;, &lt;a href="https://open.spotify.com/album/4yXX0zQxFl88hAV7FauBZ7"&gt;@spotify&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 2022 artificial intelligence (AI) has made some major strides in its capabilities. Largely in the two following area&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt to text generation&lt;/li&gt;
&lt;li&gt;Prompt to image generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Collectively, these "prompt-to-output" models are designed to take a prompt (or a question) and generate a finished product in either text (like a clickbait headline, or programming code) or image form (an illustrated art image, or stock images) based on that prompt. &lt;/p&gt;

&lt;p&gt;This type of AI is incredibly powerful due to how simple it is to use, and has the potential to revolutionise many different fields, and can literally feel like magic.&lt;/p&gt;

&lt;p&gt;An example would be how a single prompt like&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://twitter.com/GuyP/status/1598020781065527296"&gt;Hey, I'm trying to come up with some interesting, fantastical way of decorating a living room for a design project&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Can be used to generate the following image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zN5fS8AG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuhms551zz95sqmbiw3n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zN5fS8AG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuhms551zz95sqmbiw3n.jpg" alt="Example of a living room, generated with the previous prompt with both chatGPT and midjourney combined" width="880" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or write business plans for a website&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TrJZjf19--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3nzi6umbwd8rdqhfpzp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TrJZjf19--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3nzi6umbwd8rdqhfpzp.jpg" alt="ChatGPT prompt, generating a large business plan for a turbo tax competitor" width="880" height="1155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or even be used to help &lt;a href="https://andrewmayneblog.wordpress.com/2022/11/30/collaborative-creative-writing-with-openais-chatgpt/"&gt;generate entire novels&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With many more new unknown use cases to come.&lt;/p&gt;

&lt;p&gt;Because its full scope of its capabilities really requires you to try it, to better understand the potential for yourself. I would highly recommend giving these services a try, while its still free at &lt;a href="https://chat.openai.com/chat"&gt;https://chat.openai.com/chat&lt;/a&gt; (it may turn to a paid service in future) &lt;/p&gt;

&lt;h2&gt;
  
  
  Holy Crap, can it generate anything?
&lt;/h2&gt;

&lt;p&gt;Yes, but that includes dreaming up lies. &lt;/p&gt;

&lt;p&gt;In an extremely convincing fashion that even experts can find it hard to tell the difference.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--EVe8h28x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1165282125295190016/h0lTAKvp_normal.jpg" alt="Dare Obasanjo 🐀 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Dare Obasanjo 🐀
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @carnage4life
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      ChatGPT is an AI that has mastered a unique human skill, bullshitting. It knows what the shape of a good answer looks like but often not the details.&lt;br&gt;&lt;br&gt;But we’re now learning that even technical people are happy with an answer that looks right instead of is right.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      10:41 AM - 05 Dec 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1599715584392167425" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1599715584392167425" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1599715584392167425" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;It can also be very stubborn in how it present itself, so as hard as i tried to get it to help me write this article, unfortunately it just couldn't be "me".&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3akzHNAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1538993470047666176/QezDi3-9_normal.png" alt="PicoCreator profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        PicoCreator
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      So can &lt;a href="https://twitter.com/hashtag/ChatGPT"&gt;#ChatGPT&lt;/a&gt;  do all my article writing and documentation?&lt;br&gt;&lt;br&gt;Yes, it can help bounce ideas, and drafts. And basically be a writing buddy&lt;br&gt;&lt;br&gt;No, it seems to be very very stubborn in writing in a certain set of styles. And not one that I am used to, or is me&lt;br&gt;&lt;br&gt;But is it useful? YES
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      04:08 AM - 06 Dec 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1599978993205530625" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1599978993205530625" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1599978993205530625" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;In a weird way, you should view these AI as unique assistant who ...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is a naive smart kid&lt;/li&gt;
&lt;li&gt;who is slightly high or dreaming in the clouds&lt;/li&gt;
&lt;li&gt;have very unique and flawed views&lt;/li&gt;
&lt;li&gt;and is very willing to bluff their way, in providing a response, even if they dun have the answers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ever dealt with such an individual, you know that there are random nuggets of pure gold wisdom that they will say from time to time, and bulls#!t you will need to filter, as it dreams up the answer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The later act of filtering, is potentially the biggest danger of this new technology.&lt;/p&gt;

&lt;p&gt;As it can be very very difficult, because of how convincing the AI structure its answer, if you are not an expert in the respective field. &lt;/p&gt;

&lt;p&gt;For example, as an experiment, I asked it questions in domains i had zero (or even negative) expertise, like cooking or chemistry. And I realised I have nearly no capability, in validating its bluffs.&lt;/p&gt;

&lt;p&gt;This is despite my best attempts to google and fact check it at times, where it may give sometimes answers that is slightly different, but I have honestly no idea if its right or wrong. Unless I try it myself.&lt;/p&gt;

&lt;p&gt;Which can potentially be dangerous, seeing as how it can get things wrong in my area of expertise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How is that possible, wasn't it trained on nearly a terabyte of data?
&lt;/h2&gt;

&lt;p&gt;Yes, but it is not thinking, it is dreaming.&lt;/p&gt;

&lt;p&gt;To elaborate, let’s start with an oversimplified explanation on how these AI models fundamentally work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First the AI is given a large dataset of articles or images from various sources, it uses this data to provide the foundation of its knowledge, loosely connected together. This is then trained together with possible prompts and responses given by the user.&lt;/p&gt;

&lt;p&gt;Once this is completed, we have a working neural network or trained model with all this loosely connected data. Frozen in time.&lt;/p&gt;

&lt;p&gt;When a prompt or question is given, the AI is not thinking of an answer or deliberating from the trained model. In a way, it is hallucinating the answer using the snapshot of knowledge it has.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, lets use a human analogy instead&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The human analogy, would be your brain right now, with all your past knowledge, or experiences in school or in life. Snapshot and recorded, in your head. Minus your emotions, and your feelings, or anything hormonal related.&lt;/p&gt;

&lt;p&gt;When a prompt is given, imagine it as being told to you, before going to sleep into a dream. For example "go buy milk tomorrow, when you wake up".&lt;/p&gt;

&lt;p&gt;For most folks, sleep is when they as an individual have little to no control with how the story goes (ignoring the lucky few lucid dreamers).&lt;/p&gt;

&lt;p&gt;In this state, your mind in its dream state, starts to take that last phrase that is stuck in your head, and starts dreaming a story around that phrase.&lt;/p&gt;

&lt;p&gt;Where you are an individual inside a dream, and your brain the dream world doing its best to be as convincing as possible that its real and not a dream world, continuously writing the story back and forth with your interactions and actions.&lt;/p&gt;

&lt;p&gt;All while using the snapshot of all your past knowledge and experience, in your brain. To make your dreamworld as realistic as possible&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flipping it back around&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You, with your prompts is a participant in a dream.&lt;br&gt;
With the AI being the dream world, trying its best to be, as convincingly as possible, a good dream. Where it answer your questions in a very believable manner.&lt;/p&gt;

&lt;p&gt;And just like your dream world, it can be at times entirely realistic, and factually accurate, or as imaginary (and perhaps exciting) as it wants to be&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As such, this dreamlike state, allows it both to make basic math mistakes with high confidence&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IHD_K0C3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Fi8fcczUAAArjfs.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--qanccNpP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/531828329775505408/zuvpcuiJ_normal.jpeg" alt="Ioannis Papapanagiotou profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Ioannis Papapanagiotou
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @ipapapa
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Before we all get excited about ChatGPT, I asked a simple elementary school math question. It gave me a wrong answer, x=2.25. The correct answer is x=2. The error is in the arrow. How come AI be wrong? 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      04:18 AM - 02 Dec 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1598532071990796289" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1598532071990796289" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1598532071990796289" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;To hilariously answer questions as a cat when instructed&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZQ3aknXi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Fi9O8DaacAAELXx.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s---kQg1tqK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1024828702310330368/HbIftLMQ_normal.jpg" alt="Jeff Yang profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Jeff Yang
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @originalspin
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      ChatGPT really sold it with that final Meow! 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      07:41 AM - 02 Dec 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1598583192050864153" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1598583192050864153" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1598583192050864153" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;To very impressively, even &lt;a href="https://www.engraved.blog/building-a-virtual-machine-inside/"&gt;emulating interactions with a virtual machine (rather accurately!)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean for the future of work?
&lt;/h2&gt;

&lt;p&gt;It's difficult to predict exactly how the advancements in AI will impact the future of work, but there are some potential scenarios to consider.&lt;/p&gt;

&lt;p&gt;One thing that seems clear is that the current wave of "prompt-to-output" models is having its "iPhone revolution moment". Just as the iPhone changed how everyday people interact with software and ushered in the era of smartphones, prompt-to-output models have the potential to change how we work and interact with tools.&lt;/p&gt;

&lt;p&gt;It has already started fundamentally changing industries, today. Especially in SEO copywriting, and stock illustration / photo industries.&lt;/p&gt;

&lt;p&gt;The next wave could manifest in waves of AI assistant work tools and practical enhancements to existing tools, ultimately making automation more accessible and lowering the barrier of entry for performing tasks.&lt;/p&gt;

&lt;p&gt;Regardless of the forms of changes, this is a significant development and every startup founder (including me) and product owner should be taking note of how to integrate AI into their business. Just as the smartphone/iPhone era made entire industries and tools obsolete and created new domains of app-based tools, the rise of AI has the potential to do the same.&lt;/p&gt;

&lt;p&gt;However, it's important to note that not all tools will be impacted by AI. Some industries, may not see much change beyond a new skin or UI. Only time will tell which side your business falls on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: if you are feeling FOMO, and is in the tech industry, chill - the iPhone and app revolution happened in months and years, not days. Do go on with your life, and your holiday plans.  &lt;/p&gt;

&lt;p&gt;As long as you are not ignoring the potential changes, and making steps towards it - you are good.&lt;/p&gt;

&lt;p&gt;If you are outside the tech industry, keep a look out, be ready to learn new tools, across the next few years - that may change your industry. And be prepared to learn and use them to your advantage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;~ Until next time 🖖 live long and prosper&lt;/p&gt;

</description>
      <category>openai</category>
      <category>beginners</category>
      <category>tooling</category>
      <category>ai</category>
    </item>
    <item>
      <title>SAINE is Mathematically One of the Best WORDLE Starting Words - Here's Why</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Fri, 15 Apr 2022 22:52:57 +0000</pubDate>
      <link>https://forem.com/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396</link>
      <guid>https://forem.com/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396</guid>
      <description>&lt;p&gt;TLDR: I wrote a Wordle solver bot with Javascript and UIlicious. You can run &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;this snippet&lt;/a&gt; any day to get your daily Wordle solution. Try and see if you can get a better score than the bot! &lt;strong&gt;Feel free to edit it and optimise the solver algorithm!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The wordler solver is covered in 3 parts, which covers the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI interaction code &lt;a href="https://uilicious.com/blog/automate-wordle-via-uilicious/" rel="noopener noreferrer"&gt;(link here)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wordle Statistical model, and the math behind it (this article) &lt;/li&gt;
&lt;li&gt;Unit testing and benchmarking of the wordle solver (@ TODO) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the statistical examples can be found at the following link:  &lt;a href="https://uilicio.us/wordle-statistics-sample" rel="noopener noreferrer"&gt;https://uilicio.us/wordle-statistics-sample&lt;/a&gt;&lt;br&gt;
And is generated via code used here: &lt;a href="https://github.com/uilicious/wordle-solver-and-tester" rel="noopener noreferrer"&gt;https://github.com/uilicious/wordle-solver-and-tester&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic5us2bzeu7idt659wm8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic5us2bzeu7idt659wm8.gif" alt="Cat reading the art of war"&gt;&lt;/a&gt;&lt;/p&gt;
Cat planning our Wordle strategy



&lt;h1&gt;
  
  
  Our WORDLE strategy
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer, I do not claim for this to be the best WORDLE strategy (yet), But it is a rather good one =)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before going into the statistics, let's first cover our WORLDE strategy.&lt;/p&gt;

&lt;p&gt;One thing that humans do really well that classic computers are bad at is to "intuitively" figure things out. Unless I plan to train a neural network, the computer program I am developing will need to use a classic dictionary word list to make its guesses.&lt;/p&gt;

&lt;p&gt;The one thing that computers do well however, is memorising giant lists of words or data. And performing math on it. So let's use this to our advantage by doing the following sequence.&lt;/p&gt;

&lt;p&gt;Given the 2 word list, one full of possible answers ( ~2.3k words), and another with the full word list (13k words)...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filter out words in the possible answer list against the current game state from past guesses.&lt;/li&gt;
&lt;li&gt;Count the number of times each character appears in the answer word list in their respective word positions.&lt;/li&gt;
&lt;li&gt;From the full word list, choose the word which is most likely to find a correct character guess. Independently score it preferring words that give more information in the first 4 rounds with either an exact or partial match.&lt;/li&gt;
&lt;li&gt;Choose the highest scoring word and try it.&lt;/li&gt;
&lt;li&gt;Repeat from the top if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Also to clarify:&lt;/strong&gt; We do not memorise the previous Wordle solutions (I felt that was cheating as the system could end up just memorising the day-to-day list in sequence).&lt;/p&gt;

&lt;p&gt;While the exact details of the scoring change slightly depending on the round - to optimise things. It does not change the overall concept on a high level.&lt;/p&gt;

&lt;p&gt;So how does this work in practice? For our current iteration of the Wordle strategy, we will view this in practice step by step (without code).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F166vkmh4ewjt5bz5ygyw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F166vkmh4ewjt5bz5ygyw.gif" alt="Man lost in counting"&gt;&lt;/a&gt;&lt;/p&gt;
Counting statistics is "easy"



&lt;h1&gt;
  
  
  Our starting word: SAINE - and why you should use it
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;An alias to sain, meaning: to make the sign of the cross over (oneself) so as to bless or protect from evil or sin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There has been lots of advice on the starting 2 words. And it makes sense that this word follows with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 very common vowels&lt;/li&gt;
&lt;li&gt;All unique characters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But let's figure out why the solver chooses this word by looking at the numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvinckh9c9imnh4udoi33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvinckh9c9imnh4udoi33.png" alt="Opening word stats"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the characters in Wordle.



&lt;p&gt;Based on the statistics. "SAINE" has the highest chance of finding an exact green character match as an opening word while having 3 of the top vowels.&lt;/p&gt;

&lt;p&gt;Reading the raw distributing table can be understandably hard to digest. So let me recontextualize those numbers here. SAINE has a …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;91.06% chance of partial matching at least 1 character (yellow/green);&lt;/li&gt;
&lt;li&gt;55.94% chance of partial matching at least 2 characters (yellow/green);&lt;/li&gt;
&lt;li&gt;50.24% chance of exactly matching at least 1 character (green).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a  really high chance of getting at least one or 2 major clues. Inversely, because there are few words without A, I, and E, failure to match is a "huge clue."&lt;/p&gt;

&lt;p&gt;Not bad for an opening huh?&lt;/p&gt;

&lt;p&gt;What about other popular opening words such as "CRANE" and "ADEPT"?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfaerpv8y7nplwynqv9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfaerpv8y7nplwynqv9k.png" alt="Probability distribution table for different words"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of first word guess for SAINE, CRANE and ADEPT.



&lt;p&gt;The only key advantage, for "CRANE / ADEPT" is they both have a 0.04% chance of successfully doing a 1-word guess. I suspect the flaw of the previous public analysis was how they were limiting the opening word to the known answer list. I believe however, we should be using the full word list instead, to maximize the probabilities of clues in favor of a very narrow chance of doing a 1-word guess.&lt;/p&gt;

&lt;p&gt;More importantly, SAINE has a significantly higher chance (by  ~7%) of guessing an exact match (green) on the first try. Which is incredibly helpful as a clue.&lt;/p&gt;

&lt;p&gt;With the starting word debate out of the way, we will see how the system reacts with the different results!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrrjh4uwgz2hrdx96mv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrrjh4uwgz2hrdx96mv9.png" alt="2 wordle result, side by side"&gt;&lt;/a&gt;&lt;/p&gt;
Wordle results before and after migrating to the NYTimes.



&lt;h2&gt;
  
  
  Understanding how the 2nd guess PLUTO, leads to PAUSE
&lt;/h2&gt;

&lt;p&gt;So let us look at how the 2nd word is chosen for the answer "PAUSE" (left-hand side).&lt;/p&gt;

&lt;p&gt;We are given the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Letter I &amp;amp; N is not in the word.&lt;/li&gt;
&lt;li&gt;A is the 2nd character, E is the 5th character.&lt;/li&gt;
&lt;li&gt;S is either the 3rd or 4th character (it is not the 1st character).&lt;/li&gt;
&lt;li&gt;Only 12 possible answers are in the word list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretty standard Wordle stuff. But let's look at how does this impacts the stats on the remaining possible answers. And how “PLUTO” was chosen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dhjqs93d3skoko3t15e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dhjqs93d3skoko3t15e.png" alt="Wordle statistics"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 2nd word characters when the answer is PAUSE.



&lt;p&gt;With only 12 words left, let’s find the most efficient way to eliminate our options.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because we already have information on the letters S, E, A, our scoring system will avoid them with a penalty (marked in red).&lt;/li&gt;
&lt;li&gt;The next biggest probabilities of characters in positions we do not know about are P, U, and T, in positions 1, 3, and 4, respectively (marked in orange).&lt;/li&gt;
&lt;li&gt;Finally, because we have positions 2 and 5 solved, we can make use of this to get additional information. So let us try our best to use the characters L &amp;amp; C.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the following constraints, the only valid word to try was PLUTO. Although we know the letter O is not in the final answer, there is no word for “PLUTC”. Also, while the word PLUTO was not in the answer list, it was in the full word list, making it a valid guess.&lt;/p&gt;

&lt;p&gt;After the submission, the system now knows the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;L, T, O is not in the word (O is a confirmation of an assumption).&lt;/li&gt;
&lt;li&gt;P is the 1st character, U is the 3rd character.&lt;/li&gt;
&lt;li&gt;S is the 4th character (is it can no longer be the 3rd character).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means I no longer need stats because there is only one truth: PAUSE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtqi4x531i4ti3b09lvn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtqi4x531i4ti3b09lvn.gif" alt="Dancing futurama robot named bender"&gt;&lt;/a&gt;&lt;/p&gt;
bender dancing to a successful answer



&lt;h2&gt;
  
  
  But what is with the crazy guessing pattern? With MOHUR, BLYPE, and finally ULCER
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Mohur is a gold coin that was formerly minted by several governments, including British India and some of the princely states which existed alongside it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The statistics here is being evaluated live and the changes are dependent on the result of each round. So the result differed where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Letter S, A, I, N is not in the word.&lt;/li&gt;
&lt;li&gt;Letter E may be in positions 1, 2, 3, 4 but not 5.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Filtering out the possible answer list leads to the following statistics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrtoofpmulah7h148mau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrtoofpmulah7h148mau.png" alt="Statistics for 2nd word"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 2nd word characters. When the answer is ULCER.



&lt;p&gt;This may not make sense at first because the word does not start with C or B. With 187 words left, this is where the details of the scoring start to matter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once again, we ignore the letter E, which is the information we already know (in red).&lt;/li&gt;
&lt;li&gt;The letter R at the end has the highest weightage (of 62 words) by a large margin. Finding out the answer for this character either halves (no match) the possibility list, or one-thirds it (it matches).&lt;/li&gt;
&lt;li&gt;From here, the next step is finding the word with the most matches against other letters in the respective positions such as O at position 2 (score of 37), followed by any remaining characters. Matching either their positional list or unique letters list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may not be the most optimal choice given the known answer list. However, this is intentional because, we want to focus on the information again until we have a very high confidence in a word. And in this process, we would penalize duplicate letters to focus on reducing the number of options.(Arguably there is room for improvements here)&lt;/p&gt;

&lt;p&gt;The results from the guess were interesting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yys6267a1g40ng8xveo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yys6267a1g40ng8xveo.png" alt="Statistics for 3rd word"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 3rd-word characters when the answer is ULCER.



&lt;p&gt;As weird and confusing as the word MOHUR was, the result reduced the possibility to 12 words. Once again, it tries to prioritise’s trying new characters and giving it the obscure word of BLYPE.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BLYPE: a piece or shred of skin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This word reduces the list of possibilities down to one word - ULCER, which is the final answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side Note:&lt;/strong&gt; &lt;em&gt;If you notice, it is willing to try characters it knows that is not in the official answer list. This is intentional, and helps take into account Wordle clones because if the actual answer chosen is not within the original answer list, the system will automatically fall back to using the full word list instead. Making this more resilient against Wordle varients.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7i11k40kme3hvshmuuao.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7i11k40kme3hvshmuuao.gif" alt="Small minion shouting warning via loud hailer"&gt;&lt;/a&gt;&lt;/p&gt;
Minion issuing a warning



&lt;p&gt;&lt;strong&gt;⚠️ Code warning ahead:&lt;/strong&gt; If you were only here for the math and stats then skip to the end. The remaining content in this article consists of JS code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me the code
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;The full solving class can be &lt;a href="https://github.com/uilicious/wordle-solver-and-tester/blob/main/wordle-article-notes/WordleSolvingAlgo.js" rel="noopener noreferrer"&gt;found here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article will be focusing on the core functionality that is required to make this process work and not all parts of the code. If you have not read Part 1, &lt;a href="https://uilicious.com/blog/automate-wordle-via-uilicious/" rel="noopener noreferrer"&gt;read it here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The code here has been simplified to skip the "boilerplate" stuff.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The solver needs to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Given the current game state, filter the possible word list.&lt;/li&gt;
&lt;li&gt;Given the filtered word list, calculate the statistics.&lt;/li&gt;
&lt;li&gt;Given the statistics, and game state, score the word following the strategy.&lt;/li&gt;
&lt;li&gt;Suggest a word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let us break it down, piece by piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalize The game state object (generated in part 1)
&lt;/h2&gt;

&lt;p&gt;For every round, the game state would be generated with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.history [ ] : An array of past wordle guesses.&lt;/li&gt;
&lt;li&gt;.pos [ ]: An array of objects containing the following info.

&lt;ul&gt;
&lt;li&gt;.hintSet : set of characters that are valid "hints".&lt;/li&gt;
&lt;li&gt;.foundChar: characters that are confirmed for the given position.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is generated by using the information on the screen in part 1.&lt;/p&gt;

&lt;p&gt;However, for our use case, we would need to normalize some of the common data set we would need. Through the &lt;code&gt;_normalizeStateObj&lt;/code&gt; function, we get the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;badCharSet: Characters we know that is not in the word;&lt;/li&gt;
&lt;li&gt;goodCharSet: Characters we have confirmed are in the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is easily generated by iterating &lt;code&gt;.history&lt;/code&gt;, and the &lt;code&gt;.pos&lt;/code&gt; data for building the good character list first. Then using that to build the bad character list inversely against the historical word list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Given the state object, normalize various values, using the minimum "required" value.
 * This does not provide as much data as `WordleSolvingAlgo` focusing on the minimum required
 * to make the current system work
 * 
 * @param {Object} state 
 * 
 * @return {Object} state object normalized
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_normalizeStateObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Setup the initial charset&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets build the good charset&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wordLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&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;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;hintSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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="c1"&gt;// Lets iterate history and build badCharSet&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&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;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&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="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;w&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;w&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// check the individual char&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// If char is not in good set&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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="c1"&gt;// its in the bad set&lt;/span&gt;
                &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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="c1"&gt;// Return the normalize state object&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&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;h2&gt;
  
  
  Filtering the possible word list
&lt;/h2&gt;

&lt;p&gt;Now that we have the current game state, let us look into filtering the word list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Given the wordList, filter only for possible answers, using the state object.
 * And returns the filtered list. This function just returns the wordList, if state == null
 * @param {Array&amp;lt;String&amp;gt;} wordList 
 * @param {Object} state 
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip if its not setup&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;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;return&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the word length&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wordLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&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;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Filter and return&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Filtering logic&lt;/span&gt;
        &lt;span class="c1"&gt;// ....&lt;/span&gt;

        &lt;span class="c1"&gt;// all checks pass, return true&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;For the filtering logic, we first remove the words within the badCharSet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out invalid words (aka hard mode)&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;const&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// PS : this does nothing if the set is empty&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bad&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;false&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;Followed by filtering out the words with wrong hint locations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out words with wrong hint locations, for each character position&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="nx"&gt;wordLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the word character&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&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="c1"&gt;// Check if the chracter, conflicts with an existing found char (green)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sChar&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&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;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if the character is already a known mismatch (yellow, partial match)&lt;/span&gt;
    &lt;span class="c1"&gt;// for each position&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;const&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;hintSet&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;sChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;bad&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;false&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the subsequent words without all the known found (exact, and partial) matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out words WITHOUT the hinted chars&lt;/span&gt;
&lt;span class="c1"&gt;// PS : this does nothing if the set is empty&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;const&lt;/span&gt; &lt;span class="nx"&gt;good&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;good&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;false&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;In addition, we have a variant to filter out unique words for &lt;code&gt;filterForUniqueWordList&lt;/code&gt;. This has no character duplicates and  is used in the first few rounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wordCharSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// iterate the characters&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;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update the word charset&lt;/span&gt;
    &lt;span class="nx"&gt;wordCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// There is duplicate characters&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;wordCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;false&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;h2&gt;
  
  
  Generating the word statistics
&lt;/h2&gt;

&lt;p&gt;After filtering for all possible answers remaining, statistics are generated via &lt;code&gt;charsetStatistics( dictArray )&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is done by building an object for the type of statistics. Iterate the word list and incrementing the numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Analyze the given dictionary array, to get character statistics
 * This will return the required statistics model, to be used in guessing a word.
 * 
 * Which is provided in 3 major parts, using an object, which uses the character as a key, followed by its frequency as a number
 * 
 * - overall : Frequency of apperance of each character
 * - unique  : Frequency of apperance of each character per word (meaning, duplicates in 1 word is ignored)
 * - positional : An array of object, which provides the frequency of apperance unique to that word position
 * 
 * Note that because it is possible for the dataset to not have characters in the list / positional location,
 * you should assume any result without a key, means a freqency of 0
 * 
 * @param {Array&amp;lt;String&amp;gt;} dictArray - containg various words, of equal length
 * 
 * @return Object with the respective, overall / unique / positional stats
 **/&lt;/span&gt;
&lt;span class="nf"&gt;charsetStatistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Safety check&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;dictArray&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;throw&lt;/span&gt; &lt;span class="s2"&gt;`Unexpected empty dictionary list, unable to perform charsetStatistics / guesses`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The overall stats, for each character&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="c1"&gt;// The overall stats, for each unique charcter &lt;/span&gt;
    &lt;span class="c1"&gt;// (ignore duplicates in word)&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="c1"&gt;// The stats, for each character slot&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets initialize the positionalStats&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wordLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dictArray&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;length&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="nx"&gt;wordLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;positionalStats&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets iterate the full dictionary&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;const&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// Character set for the word&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// For each character, populate the overall stats&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="nx"&gt;wordLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Get the character&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&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="c1"&gt;// Increment the overall stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Populate the charset, for overall unique stats&lt;/span&gt;
            &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Increment each positional stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&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;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Populate the unique stats&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;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;charSet&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Increment the overall unique stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;char&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="c1"&gt;// Lets return the stats obj&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;overall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&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;This is fairly straightforward for loopings across every word and every character increment within the respective statistical count.&lt;/p&gt;

&lt;p&gt;The only gotcha is that we are unable to do a ++ increment on an object property when it's not initialized. This would result in the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This will give an exception for &lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Cannot read properties of undefined (reading 'a')&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we would need to use a simple helper function to increment our needed use case properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Increment an object key, used at various stages of the counting process
* @param {Object} obj 
* @param {String} key 
**/&lt;/span&gt;
&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&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="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;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2lkhv3vp4e62mf7vrqo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2lkhv3vp4e62mf7vrqo.gif" alt="Judges voting"&gt;&lt;/a&gt;&lt;/p&gt;
Judges giving their score



&lt;h2&gt;
  
  
  Scoring each word
&lt;/h2&gt;

&lt;p&gt;At the heart of the solver is the scoring logic. Which is ranked on every possible word input with the given statistics and state.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I do not claim that this is the most optimal word scoring function there is for Wordle. It definitely can be improved, but it’s quite good from my testing so far. =)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* The heart of the wordle solving system.
* 
* @param {Object} charStats, output from charsetStats 
* @param {String} word to score 
* @param {Object} state object (to refine score)
* 
* @return {Number} representing the word score (may have decimal places)
**/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&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;span class="c1"&gt;// Character set for the word, used to check for uniqueness&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// the final score to return&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&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="c1"&gt;// Wordle Strategy note:&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// - Penalize duplicate characters, as they limit the amount of information we get&lt;/span&gt;
    &lt;span class="c1"&gt;// - Priotize characters with high positional score, this helps increase the chances of "exact green matches" early&lt;/span&gt;
    &lt;span class="c1"&gt;//   reducing the effort required to deduce "partial yello matches"&lt;/span&gt;
    &lt;span class="c1"&gt;// - If there is a tie, in positional score, tie break it with "unique" score and overall score&lt;/span&gt;
    &lt;span class="c1"&gt;//   this tends to be relevent in the last &amp;lt;100 matches&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// - We used to favour positional score, over unique score in the last few rounds only&lt;/span&gt;
    &lt;span class="c1"&gt;//   but after several trial and errors run, we found it was better to just use positonal score all the way&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets do scoring math&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the score&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;score&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;This goes through various stages: First, we add a safety net to prevent the system from suggesting a word again (huge negative score).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Skip attempted words - like WHY ???&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;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;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;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1000&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 then iterate each character of the word, and score them respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// For each character, populate the overall stats&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="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the character&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&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="c1"&gt;// Does scoring for each character&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Penalizing words with repeated characters or known characters&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// skip scoring of known character matches&lt;/span&gt;
&lt;span class="c1"&gt;// or the attempted character hints&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;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip known chars (good/found)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip scoring of duplicate char&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;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip known chars (good/found)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip scoring of duplicate char &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;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&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="c1"&gt;// Populate the charset, we check this to favour words of unique chars&lt;/span&gt;
&lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we  calculate the score for each positional statistic with the unique character scoring being used as tie-breakers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dev Note:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// In general - we should always do a check if the "character" exists in the list.&lt;/span&gt;
&lt;span class="c1"&gt;// This helps handle some NaN situations, where the character has no score&lt;/span&gt;
&lt;span class="c1"&gt;// this is possible because the valid list will include words, that can be inputted&lt;/span&gt;
&lt;span class="c1"&gt;// but is not part of the filtered list - see `charsetStatistics`&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;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;positional&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;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;positional&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;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10000&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;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// -- Loops to the next char -- //&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the scoring function we can start putting together all of the parts for the "suggestWord" function.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faws4cy6euy3vgrbcnkah.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faws4cy6euy3vgrbcnkah.gif" alt="Simpsons lisa trying to choose a pet"&gt;&lt;/a&gt;&lt;/p&gt;
The simpsons : you've just gotta choose one



&lt;h2&gt;
  
  
  Suggesting a word (putting it together)
&lt;/h2&gt;

&lt;p&gt;We have statistics that can then be used to score words. Now, let us put it together to suggest the best scoring word.&lt;/p&gt;

&lt;p&gt;We start with being given the game state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;normalize game state;&lt;/li&gt;
&lt;li&gt;generate filtered, unique, and full word lists
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Given the minimum state object, suggest the next word to attempt a guess.
* 
* ---
* # "state" object definition
* 
* The solver, requires to know the existing wordle state information so this would consist of (at minimum)
* 
* .history[]    : an array of past wordle guesses
* .pos[]        : an array of objects containing the following info
*    .hintSet   : set of characters that are valid "hints"
*    .foundChar : characters that are confirmed for the given position
* 
* The above is compliant with the WordleAlgoTester state object format
* Additional values will be added to the state object, using the above given information
* --- 
* 
* @param {Object} state 
* 
* @return {String} word guess to perform
*/&lt;/span&gt;
&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Normalize the state object&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_normalizeStateObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Let'sLets get the respective wordlist&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fullWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&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;filteredWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filteredWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&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;uniqueWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterForUniqueWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniqueWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// As an object&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uniqueWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets do work on the various wordlist, and state&lt;/span&gt;
    &lt;span class="c1"&gt;// this is refactored as `suggestWord_fromStateAndWordList`&lt;/span&gt;
    &lt;span class="c1"&gt;// in the code base&lt;/span&gt;
    &lt;span class="c1"&gt;// ....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have the various game states and word lists, We can decide on the "statistics word list", which we use to generate the statistics model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Let's decide on which word list we use for the statistics&lt;/span&gt;
&lt;span class="c1"&gt;// which should be the filtered word list **unless** there is &lt;/span&gt;
&lt;span class="c1"&gt;// no possible answers on that list, which is possible when&lt;/span&gt;
&lt;span class="c1"&gt;// the system is being used against a WORDLE variant&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// In such a case, lets fall back to the filtered version of the "full&lt;/span&gt;
&lt;span class="c1"&gt;// word list", instead of the filtered version of the "answer list".&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&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;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[WARNING]: Unexpected empty 'filtered' wordlist, with no possible answers : falling back to full word list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[WARNING]: Unexpected empty 'filtered' wordlist, with no possible answers : despite processing from full list, using it raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&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;Once we decide on the stats word list, we receive  the stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get the charset stats&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charsetStatistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statsList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we decide the word list to use in deciding a word. We refer to this list as the "scoredList".&lt;/p&gt;

&lt;p&gt;In the first few rounds, we aim to use unique words as much as possible. Which would not include characters that we have tried previously. This may include words we know are not in the possible answer list.&lt;/p&gt;

&lt;p&gt;This is intentional as we are optimizing for information gain but instead over a small random chance of early success.&lt;/p&gt;

&lt;p&gt;However when this is emptied, or the game is in the final few rounds, we will fall back to the full list. During the final round, we will always guess using the filtered list when possible: (just give it our best answer).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sort the scored list, use unique words in first few rounds&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Use valid list from round 5 onwards&lt;/span&gt;
&lt;span class="c1"&gt;// or when the unique list is drained &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;scoredList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use filtered list in last 2 round, or when its a gurantee "win"&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;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roundLeft&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roundLeft&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&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;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&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;Once we decided on the scoring list to apply the stats, Let us score and sort it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Self reference&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Score word sorting&lt;/span&gt;
&lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scoredList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the score&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalStretch&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;aScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalStretch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// And arrange them accordingly&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;bScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;aScore&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="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="k"&gt;else&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;bScore&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;aScore&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Tie breakers - rare&lt;/span&gt;
        &lt;span class="c1"&gt;// as we already have score breakers in the algori&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;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&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="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="k"&gt;else&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;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&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;-&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="c1"&gt;// Equality tie ???&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="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="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="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;And return the highest scoring item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Return the highest scoring word guess&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;scoredList&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecswasur43zz0atud9ga.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecswasur43zz0atud9ga.gif" alt="4 pugs, dressed as the avengers"&gt;&lt;/a&gt;&lt;/p&gt;
4 pugs, dressed up as the avengers



&lt;h1&gt;
  
  
  Putting it all together
&lt;/h1&gt;

&lt;p&gt;With the UI interaction code done in part 1. Hit the "Run" button to see how our Wordle bot does!&lt;br&gt;
&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;&lt;br&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nhdfah09z5ywrtu0tr9.gif" alt="Uilicious solving a wordle"&gt;&lt;/a&gt;&lt;/p&gt;
Screen recording of the wordle being solved



&lt;p&gt;Hey, not bad, my bot solved today's Wordle!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rpx7d2xfxtn2shexy38.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rpx7d2xfxtn2shexy38.gif" alt="Robot friend hugging a human"&gt;&lt;/a&gt;&lt;/p&gt;
Human and robots as friends



&lt;h1&gt;
  
  
  Take away - for the humans?
&lt;/h1&gt;

&lt;p&gt;Because the bots will use a rather "inhuman" technique of calculating probabilities with giant dictionaries.&lt;/p&gt;

&lt;p&gt;Most humans will find that this is borderline of doing really weird and crazy guesses. Believe the math because it works.&lt;/p&gt;

&lt;p&gt;While playing for the human team, your takeaway from this article is that you should just start with the word "SAINE", or whatever words you would like.&lt;/p&gt;

&lt;p&gt;It is up to you since this is your game after all! =) Have fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Wordling! 🖖🏼🚀&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First published &lt;a href="https://uilicious.com/blog/how-saine-mathematically-beats-crane-as-one-of-the-best-wordle-starting-word/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fun</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Created a Bot to Solve Wordle so I Never Have to Ever Again</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Sat, 12 Feb 2022 05:05:17 +0000</pubDate>
      <link>https://forem.com/uilicious/i-suck-at-wordle-so-i-solved-it-with-js-and-uilicious-4hfc</link>
      <guid>https://forem.com/uilicious/i-suck-at-wordle-so-i-solved-it-with-js-and-uilicious-4hfc</guid>
      <description>&lt;p&gt;TLDR: I wrote a Wordle solver bot with Javascript and UIlicious. You can run &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;this snippet&lt;/a&gt; any day to get your daily Wordle solution. Try and see if you can get a better score than the bot! &lt;strong&gt;Feel free to edit it and optimise the solver algorithm!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The wordler solver is covered in 3 parts, which covers the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI interaction code (this article)&lt;/li&gt;
&lt;li&gt;Wordle Statistical model, and the math behind it &lt;a href="https://dev.to/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396"&gt;(link here)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Unit testing and benchmarking of the wordle solver (@ TODO) &lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;&lt;br&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nhdfah09z5ywrtu0tr9.gif" alt="Uilicious solving a wordle"&gt;&lt;/a&gt;&lt;/p&gt;
You can edit or rerun the script, by clicking on the GIF



&lt;p&gt;If you haven't already heard of Wordle, it's a fun puzzle game that challenges you to guess a valid five-letter word in six tries or less.&lt;/p&gt;

&lt;p&gt;My wife is really addicted to Wordle, and she's been bugging me to try it every day. I suppose for her, there's a thrill in getting the right word in the least amount of tries. And because we're both gamers and math-nerds, naturally we talk about strategies to solve Wordle in the least number of guesses. But when it comes to actually putting those theories to use and trying my hand at Wordle, I came to realise...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I suck at Wordle!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;English isn't my strong suite per say...&lt;/p&gt;

&lt;p&gt;I speak Javascript better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fharry-potter-allora.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fharry-potter-allora.gif" alt="harry-potter-allora"&gt;&lt;/a&gt;I speak many languages, including Python ;-)&lt;/p&gt;

&lt;p&gt;So what better way to solve Wordle, then to write a program to solve it for me. HA!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fbill-nye-mind-blown.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fbill-nye-mind-blown.gif" alt="Bill Nye shouting "&gt;&lt;/a&gt;Let's science this @*#$!&lt;/p&gt;

&lt;p&gt;After a weekend of hacking I finshed coding up my Wordle solver bot using JS and UIlicious, and I would say it works pretty well, and has managed to make the correct guess mostly within 3-4 tries so far. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-10-at-4.42.13-PM-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-10-at-4.42.13-PM-2.png" alt="A solved Wordle using UIlicious"&gt;&lt;/a&gt;&lt;/p&gt;
Tada, solved wordle with a bot, on UIlicious



&lt;p&gt;It has mostly stirred up my wife's competitive spirit and annoyed her, since the bot does better than her on most days. But at least she's not bugging me everyday to play Wordle anymore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fwomanyellingcat-1573233850.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fwomanyellingcat-1573233850.jpg" alt="Women yelling at cat"&gt;&lt;/a&gt;Is it cheating to write a bot? No, I'd say it's totally fair game!&lt;/p&gt;

&lt;p&gt;Curious to find out how the Wordle solver work? Let me explain!&lt;/p&gt;
&lt;h2&gt;
  
  
  Basic setup
&lt;/h2&gt;

&lt;p&gt;We're going to use &lt;a href="https://uilicious.com" rel="noopener noreferrer"&gt;UIlicious&lt;/a&gt; to interact with the Wordle website, fill in our guesses, and evaluate the hints. While UIlicious is primarily a cross-browser UI testing tool, nobody said that's all you can use it for, so why not solve some Wordles with it. &lt;/p&gt;

&lt;p&gt;Let's setup our solver bot on &lt;a href="https://snippet.uilicious.com" rel="noopener noreferrer"&gt;UIlicious Snippets&lt;/a&gt; - think of it as Codepen, but for UI testing, and it's entirely free to use for executing tests on Chrome. UIlicious lets you run Javascript, so I'm going to be writing the solver in JS.&lt;/p&gt;

&lt;p&gt;The first thing we'll have to do get the bot to navigate to the Wordle site. We use the &lt;code&gt;I.goTo&lt;/code&gt; command to go to the official Worldle site (&lt;a href="https://www.powerlanguage.co.uk/wordle/" rel="noopener noreferrer"&gt;https://www.powerlanguage.co.uk/wordle/&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.powerlanguage.co.uk/wordle/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit: Since the acquisition of Wordle by NY Times, there's now a cookie banner, which we need to either accept or dismiss, using the &lt;code&gt;I.click&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// to reject cookies and close the cookie banner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's just make sure the app has loaded, with quick check for the phrase "Guess the Wordle" using the &lt;code&gt;I.see&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guess the Wordle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's dismiss the tutorial popup by using &lt;code&gt;I.click&lt;/code&gt; with a pixel offset to click outside the popup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// clicks 40px down and 80px right from the top-left corner of the webpage&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/html/body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we'll just hit that "Run" button to run the browser automation script, and make sure this brings us to an empty Wordle board to play with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.26.30-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.26.30-AM.png" alt="Navigating to Wordle with UIlicious"&gt;&lt;/a&gt;Now we're ready to Wordle!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Ok, now let's dive into writing more complex parts of the solver.&lt;/p&gt;

&lt;p&gt;The bot will need to be able to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter letters into Wordle&lt;/li&gt;
&lt;li&gt;Read the hints from Wordle&lt;/li&gt;
&lt;li&gt;Find the solution based on the hints (I'll explain this in a follow up post)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FuYqsM9uIyuYAAAAC%2Fsimple-easy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FuYqsM9uIyuYAAAAC%2Fsimple-easy.gif" alt="Man snapping fingers, saying "&gt;&lt;/a&gt;This will be a piece of cake!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Entering letters into Wordle
&lt;/h2&gt;

&lt;p&gt;We've only got up to six tries to guess the Wordle, so we'll start with a simple &lt;code&gt;for&lt;/code&gt; loop to make 6 attempts to guess the word:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// solver code will go in here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're going to generate a couple of words and enter our guesses into the game. I'm not going to dive into how &lt;code&gt;solver.suggestWord(gameState)&lt;/code&gt; works at the moment, but what it basically does is suggest a word based on the previous guesses and the revealed hints. I'll explain how the solver works in a follow-up post, stay tuned!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
  &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After computing the &lt;code&gt;guessWord&lt;/code&gt;, I need the bot to enter the word into the game board, which is pretty straightforward. I use the command &lt;code&gt;I.type&lt;/code&gt; to enter the guess word, and &lt;code&gt;I.pressEnter&lt;/code&gt; to submit the guess. I've also added a small wait for the game board to finish its animation and allow input for the next guess, because the solver can be too fast sometimes and might attempt its next guess before the game board is ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ... &lt;/span&gt;
  &lt;span class="c1"&gt;// Does some stuff to get gameState&lt;/span&gt;

  &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
  &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&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="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
  &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&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="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
  &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reading the hints from Wordle
&lt;/h2&gt;

&lt;p&gt;The next important step is after each guess, we need to evaluate the hints to make our next guess. This is a bit trickier, as we'll need to do a bit of scraping to get our hints from the HTML. &lt;/p&gt;

&lt;p&gt;First, lets grab each row of the game board. The Wordle app is entirely written using Web Components (impressive!), so we'll need to make use of &lt;code&gt;document.querySelector&lt;/code&gt; and &lt;code&gt;.shadowRoot&lt;/code&gt; to dive into the &lt;code&gt;&amp;lt;game-app&amp;gt;&lt;/code&gt; element and grab each of the &lt;code&gt;&amp;lt;game-row&amp;gt;&lt;/code&gt; element which stores important information about the revealed hints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-theme-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#board&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what we'll get when we run this query in the browser console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.39.42-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.39.42-AM.png" alt="Inspecting the Wordle game-row element"&gt;&lt;/a&gt;Oh look what we have here!&lt;/p&gt;

&lt;p&gt;Each of these &lt;code&gt;game-row&lt;/code&gt; element has two very useful data attributes that we want: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_letters&lt;/code&gt;: tells you guess that you've made for this row, e.g. &lt;code&gt;"hello"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_evaluation&lt;/code&gt;: an array of hints for each letter, e.g. &lt;code&gt;["absent", "present", "correct", "absent", "absent"]&lt;/code&gt;. &lt;code&gt;"correct"&lt;/code&gt; if the letter is in the correct position. &lt;code&gt;"present"&lt;/code&gt; if the letter is in the word, but is in the wrong position. And &lt;code&gt;"absent"&lt;/code&gt; if the letter isn't in the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we're going to build a &lt;code&gt;state&lt;/code&gt; object to store all of these information and pass it to our solver. The code below is nothing too fancy. We'll wrap this in a function because we want to run it after each guess to get the updated hints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// We prepare the state object which we would want to return&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&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="c1"&gt;// Lets get the list of result rows from the UI DOM&lt;/span&gt;
    &lt;span class="c1"&gt;// unfortunately this is more complicated then needed due to shadowDoms&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-theme-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#board&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets build the state by reading each row&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;rowList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&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;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="c1"&gt;// a row can be blank, if there was no guess made&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;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_letters&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;_letters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt; &lt;span class="o"&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;_letters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Add it to history list&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&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;word&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Iterate each char in the word&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&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="k"&gt;if&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;_evaluation&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="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absent&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="c1"&gt;// does nothing&lt;/span&gt;
                    &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;if&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;_evaluation&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="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;present&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;hintSet&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;c&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;if&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;_evaluation&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="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;correct&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&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;foundChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&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="k"&gt;throw&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unexpected row evaluation : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&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;_evaluation&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="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Configure the final "allCorrect" state&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allCorrect&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="c1"&gt;// And return the state obj&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&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;This piece of code above needs to be executed the browser, so I need to tell the Wordle bot (which runs on a sandboxed NodeJS instance) to run the funciton in the browser using &lt;code&gt;UI.execute&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&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;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// put the above code snippet in here to get the game state&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;Okay, now we want to use this function to get the game state after each guess, so let's go back to our &lt;code&gt;for&lt;/code&gt; loop and update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// get the game state&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// if we got all the letters correct, we've won, hurray!&lt;/span&gt;
    &lt;span class="c1"&gt;// exit the loop in that case&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;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
    &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&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="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&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="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whew! Now that the bot can enter its guesses into the board and get the revealed hints, we can work on writing the algorithm to solve the Wordle in six attempts or less!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Finding the solution based on the hints... in a follow up post!
&lt;/h2&gt;

&lt;p&gt;Writing the solver algoritm is the biggest challenge, and a whole lot of fun. I spent quite a bit of time reading up on optimal strategies (there's even papers written about this), and tweaking the algoritm. But this deserves a post on it's own, so I'm going to write a follow up post after, stay tune!&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting everything together
&lt;/h3&gt;

&lt;p&gt;Finally, let's put together the wordle solver:&lt;/p&gt;

&lt;p&gt;Before the start of the &lt;code&gt;for&lt;/code&gt; loop, we'll initialise the solver with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solver&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;WordleSolvingAlgo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after we exit the &lt;code&gt;for&lt;/code&gt; the loop, we want to query the game state again. At this point, we've either guessed the correct word in less than 6 attempts, or have made 6 guesses but don't know what's the final outcome. We'll get the final game state one more time, and report the outcome using &lt;code&gt;TEST.log.pass&lt;/code&gt; if the bot won, or &lt;code&gt;TEST.log.fail&lt;/code&gt; if the bot lost game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// this comes after exiting the for loop...&lt;/span&gt;

&lt;span class="c1"&gt;// get the final game state&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// is it all correct? &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;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CONGRATS!!!&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEXT WORDLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The wordle is : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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="c1"&gt;// DARN! Try better next time!&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The last guess was : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;Here's what everything looks altogther (except for the solver algo part!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Open Wordle!&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.powerlanguage.co.uk/wordle/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// to reject cookies and close the cookie banner&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;span class="c1"&gt;// Is Wordle loaded?&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guess the Wordle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Solve Wordle!&lt;/span&gt;
&lt;span class="nf"&gt;runTheWordleSolver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;//----------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;// This function runs the wordle solver,&lt;/span&gt;
&lt;span class="c1"&gt;// which will make up to 6 attempts to guess the word&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runTheWordleSolver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// initialise the solver&lt;/span&gt;
    &lt;span class="c1"&gt;// stay tune for more about the solving algorithm!&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solver&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;WordleSolvingAlgo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&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;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// make up to 6 guesses&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;r&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;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// get the game state (includes the revealed hints)&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// if we got all the letters correct, we've won, hurray!&lt;/span&gt;
        &lt;span class="c1"&gt;// exit the loop in that case&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;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
        &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&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="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&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="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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="c1"&gt;// at this point, we either guessed the correct word in less than 6 attempts, or have made 6 guesses and don't know what's the outcome yet.&lt;/span&gt;

    &lt;span class="c1"&gt;// get the final game state&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// is it all correct? &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;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// CONGRATS!!!&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEXT WORDLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The wordle is : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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="c1"&gt;// DARN! Try better next time!&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The last guess was : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;h2&gt;
  
  
  Drumroll... The moment of Truth!
&lt;/h2&gt;

&lt;p&gt;Let's smash the "Run" button and see how our Wordle bot does!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/embed/test/public/N5qZKraAaBsAgFuSN8wxCL?stepNum=22&amp;amp;autoplay=0" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffkk0t42h4ns0nmwpyxz8.png" alt="Solved Wordle"&gt;&lt;/a&gt;&lt;/p&gt;
Hey, not bad, my bot solved today's Wordle in 4 attempts!



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FvMCk9U93WkcAAAAC%2Fjim-carrey-aceventura.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FvMCk9U93WkcAAAAC%2Fjim-carrey-aceventura.gif" alt="Jim Carrey says "&gt;&lt;/a&gt;Mmmmm, sweet sweet glory!&lt;/p&gt;

&lt;p&gt;Stay tuned for part 2. I'll explain the solver engine works, and the math behind it. Or if you want to dive into the full code yourself, you can check it out &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;over here&lt;/a&gt;, and maybe even replace &lt;code&gt;WordleSolvingAlgo&lt;/code&gt; with your own algorithm to solve Wordle: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;At the mean time ….&lt;br&gt;
&lt;strong&gt;Happy Wordling! 🖖🏼🚀&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fun</category>
      <category>beginners</category>
    </item>
    <item>
      <title>live migrating VM's - cool tech for the future, that you should avoid using today (Review &amp; Benchmark of GCP E2 Instances)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Mon, 16 Dec 2019 15:44:43 +0000</pubDate>
      <link>https://forem.com/uilicious/live-migrating-vm-s-cool-tech-for-the-future-that-you-should-avoid-using-today-review-benchmark-of-gcp-e2-instances-5247</link>
      <guid>https://forem.com/uilicious/live-migrating-vm-s-cool-tech-for-the-future-that-you-should-avoid-using-today-review-benchmark-of-gcp-e2-instances-5247</guid>
      <description>&lt;p&gt;&lt;strong&gt;Should I buy summary: NO&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Unless you are using GCP E2 specifically for, non-CPU dependent burst workloads (eg. background tasks), temporary autoscale, or are planning to use committed use discount - stay with N1/N2.&lt;/p&gt;

&lt;p&gt;For nearly all other workloads, it makes no economic sense to use E2, due to its approximately 50% reduction in CPU performance (+/-).&lt;/p&gt;

&lt;p&gt;If you want the raw numbers, skip the prelude section and go straight to the benchmark section below. &lt;a href="https://github.com/uilicious/public-datalab/tree/master/benchmark/google-cloud/e2-instance"&gt;or alternatively our github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However after benchmarking it, and reviewing the tech behind it. I realized there is a lot more then meets the eye - how E2 could however potentially be game-changing for cloud providers in the future ... Assuming they have the "courage" to use it ...&lt;/p&gt;

&lt;p&gt;(read on to learn more)&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wF0vuXPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr31b1zno4eu6nqn6yz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wF0vuXPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr31b1zno4eu6nqn6yz6.png" alt="Sample Uilicious server usage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: Understanding the VM workload
&lt;/h1&gt;

&lt;p&gt;Before understanding E2, you will need to understand a typical VM workload.&lt;/p&gt;

&lt;p&gt;Where most server VM workloads, are underutilized "most of the time", with the occasional bump in resource consumption, during certain hours of the day, or when they appear on Reddit.&lt;/p&gt;

&lt;p&gt;This can be easily be observed by looking at most VM resource usage charts.&lt;/p&gt;

&lt;p&gt;The chart above is a browser test automation server, attached to one of several clients within &lt;a href="https://ulicious.com"&gt;ulicious.com&lt;/a&gt;. Notice how this server spikes twice with a small gap in between. &lt;/p&gt;

&lt;p&gt;This is extremely typical for work applications representing the start of the workday, followed by lunch, and the rest of the workday (after offsetting time of day, for different timezone)&lt;/p&gt;

&lt;p&gt;Also note, on how the system is idle under 40% most of the time (running some background tasks). &lt;/p&gt;

&lt;p&gt;What this means is that, for most data centers, thousands of servers are paid for in full, but are underutilized, and burning through &lt;a href="https://www.datacenterknowledge.com/archives/2016/06/27/heres-how-much-energy-all-us-data-centers-consume"&gt;GigaWatts of electricity&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PRimn8sX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjogx5yglfozhv07gzn3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PRimn8sX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjogx5yglfozhv07gzn3.jpeg" alt="Sunrise in the clouds"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: The auto-scaling cloud era
&lt;/h1&gt;

&lt;p&gt;For infrastructure providers to lower overall cost, and pass these savings to their customers, as a competitive advantage. New ideas came along on how to safely maximize the usage of idle resources.&lt;/p&gt;

&lt;p&gt;First came auto-scaling, where VM's can be added only when needed, allowing cloud consumers to allocate only the bare minimum server resources required to handle the idle workload, and buffer surges to request as auto-scaling kicks in (there is a time lag). &lt;/p&gt;

&lt;p&gt;Significantly cutting down on idle resource wastage.&lt;/p&gt;

&lt;p&gt;This change, along with changes in the billing model. Started the "cloud revolution".&lt;/p&gt;

&lt;p&gt;And it was only the beginning...&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nmf88ALf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ultuikezn3d8g8j5dxbw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nmf88ALf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ultuikezn3d8g8j5dxbw.gif" alt="Pikachu Dancing - every day I'm shufflin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: Shuffling short-lived servers
&lt;/h1&gt;

&lt;p&gt;New ideas were then subsequently introduced for a new type of server workload, which can be easily shuffled around, to maximize CPU utilization and performance for all users.&lt;/p&gt;

&lt;p&gt;The essence of it is simple, slowly put as many applications as possible into a physical server, until it approaches near its limits (maybe. 80%), once it crosses the limit, pop out an application and redistribute to another server - either by immediately terminating it, or waiting for its execution to be completed and redirecting its workload.&lt;/p&gt;

&lt;p&gt;This is achieved either through extremely short-lived tasks (eg. Function as a service, serverless). Or VMs which are designed to be terminated when needed (eg. Pre-emptible, spot instances).&lt;/p&gt;

&lt;p&gt;The downside of the two approaches given above, however, is that the application software may need to be re-designed to support such workload patterns.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W0LDfCLh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dem4qq2mw6joqemi9i8x.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W0LDfCLh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dem4qq2mw6joqemi9i8x.jpeg" alt="Broken Glass, metaphor to broken rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How E2 - changes the rules of cloud shuffling
&lt;/h1&gt;

&lt;p&gt;To quote the E2 announcement page ...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After VMs are placed on a host, we continuously monitor VM performance and wait times so that if the resource demands of the VMs increase, we can use live migration to transparently shift E2 load to other hosts in the data center.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without getting too deep into how much &lt;a href="https://cloud.google.com/blog/products/compute/understanding-dynamic-resource-management-in-e2-vms"&gt;&lt;del&gt;pretty crazy and awesome&lt;/del&gt; engineering goes into writing a new hypervisor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a nutshell, what E2 does, is enable google to abstract a live running VM from its CPU hardware. Performing migration in between instances with near-zero ms downtime (assumption). &lt;/p&gt;

&lt;p&gt;Your VM servers can be running on physical server A in the first hour, and be running in physical server B in the next hour as its resources are being used up.&lt;/p&gt;

&lt;p&gt;So instead of shuffling around a specially designed VM workload. It can shuffle around any generic VM workload instead. Allowing much better utilization of resources, with lower cost, without reducing the user experience ... Or at least it should in theory ... &lt;/p&gt;

&lt;p&gt;Onto the benchmarks!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note, while live migration's of VM's are not new tech. This marks the first time it is integrated as part of a cloud offering, to lower the cost of the product. Also, for those who have used it before, the list of issues is endless - which google presumingly has resolved for their custom E2 instances.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Benchmarking: Show me the numbers!
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Full details on the benchmark raw numbers the steps involved can be found at &lt;a href="https://github.com/uilicious/public-datalab/tree/master/benchmark/google-cloud/e2-instance"&gt;GitHub link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following benchmarks were performed in &lt;code&gt;us-central1-f&lt;/code&gt; region. Using &lt;code&gt;N1/N2/E2-standard-4&lt;/code&gt; instances. With &lt;code&gt;N1-standard-4&lt;/code&gt; serving the baseline for comparison.&lt;/p&gt;

&lt;p&gt;Covering the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sysbench CPU&lt;/li&gt;
&lt;li&gt;Sysbench Memory&lt;/li&gt;
&lt;li&gt;Sysbench Mutex&lt;/li&gt;
&lt;li&gt;Nginx + Wrk&lt;/li&gt;
&lt;li&gt;Redis-Benchmark&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sysbench CPU
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EGA3OkHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nv7bl87k9yu6xmjcmhnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EGA3OkHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nv7bl87k9yu6xmjcmhnp.png" alt="CPU benchmarking summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While its no surprise that a new hypervisor designed to share CPU's across multiple workloads would be "slower". A 69% reduction might be too much for most people to stomach.&lt;/p&gt;

&lt;p&gt;However, that is not the only detail to keep track of&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gw7sliJ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrrfws4e1nwg6sm7pj7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gw7sliJ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrrfws4e1nwg6sm7pj7u.png" alt="E2 cpu run result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E2 also sees the largest variance in request statistics, across &lt;code&gt;min/avg/95 percentile&lt;/code&gt;. This is in contrast to NX series benchmark (below) which these 3 numbers would be mostly the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iRtEebc_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvzeh1pjgysmntyaf5d5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iRtEebc_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvzeh1pjgysmntyaf5d5.png" alt="N1 cpu run result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sysbench Memory / Mutex
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IzAjrfjl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/81ldb4pgnvp6gsb2tsvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IzAjrfjl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/81ldb4pgnvp6gsb2tsvw.png" alt="Memory benchmark summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the plus side, it seems like E2 instances, with the newer generation of memory hardware and clock speeds, blow pretty much N1/N2 instance workload out of the water. By very surprisingly large margins.&lt;/p&gt;

&lt;p&gt;So it's +1/-1 for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload benchmark: Nginx + Wrk, Redis-Benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5vL3yBM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xk0tdba1nacipm05fxhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5vL3yBM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xk0tdba1nacipm05fxhp.png" alt="Workload Benchmark Summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, despite much better memory performance. The penalty in CPU performance results in an approximate &lt;code&gt;~50%&lt;/code&gt; reduction in workload performance for even memory-based workloads.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lies, damned lies and benchmarks&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;These numbers are just indicators for comparison between equivalent GCP instance types. As your application workload may be a unique snowflake, there will probably be differences which you may want to benchmark on your own.&lt;/p&gt;

&lt;p&gt;Also this is meant for GCP to GCP comparison, and not GCP to other cloud provider comparison.&lt;/p&gt;

&lt;p&gt;Note, that as I am unable to induce a live migration event, and benchmark its performance under such load. Until we can find a way to get data on this, let's just presume its in milliseconds? maybe? (not that it changes my review)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Pricing Review: is it worth it ??
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J8rJjAke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q1s66irv6xbsyhb97fhr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J8rJjAke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q1s66irv6xbsyhb97fhr.jpeg" alt="sorry but no"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In case that was not clear: NO&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it was a poorer performer at a lower price E2 would make for a compelling offer. This, however, is what's the confusing thing about the E2 launch. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hg6q7RCy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/shabjfrldzjwayrmdrfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hg6q7RCy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/shabjfrldzjwayrmdrfl.png" alt="GCP tweet on E2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While its marketing materials say "up to 30% savings". The reality is much more complicated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qGT6azPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jcuk6vnuvb3pw34oa65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qGT6azPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jcuk6vnuvb3pw34oa65.png" alt="Instance price comparision"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or would I even dare say misleading? &lt;/p&gt;

&lt;p&gt;You see for N1/N2 instances, they receive &lt;a href="https://cloud.google.com/compute/docs/sustained-use-discounts"&gt;sustained usage discount&lt;/a&gt;. That scales between 0-to-30% when used continuously for a month. With E2 instances not having any sustained use discount (as it's built-in).&lt;/p&gt;

&lt;p&gt;So in a sustained 24/7 usage not only is the cost marginally higher, it has a much worse performance profile.&lt;/p&gt;

&lt;p&gt;And unfortunately, it is this pricing structure which makes E2, a really cool tech with little to no use case. Especially considering that the VM overall capacity is expected to suffer from an approximately 50% penalty in performance.&lt;/p&gt;

&lt;p&gt;If it's not clear, I made a table to elaborate on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--en_y-xEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cc60najkitdd363rkjef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--en_y-xEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cc60najkitdd363rkjef.png" alt="E2 Usage Guidelines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So unless you read through this whole review, and tested your application performance for it. Stick to the NX series of instances. &lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UvxGjqye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/imfff534vw3a6nobvbgd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UvxGjqye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/imfff534vw3a6nobvbgd.jpeg" alt="Pug looking sad"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Overall Conclusion: Wasted opportunity
&lt;/h1&gt;

&lt;p&gt;Internally for Uilicious "pokemon collection of cloud providers" we currently see no use for E2. And will be sticking with our N1 instances. As our main GCP server.&lt;/p&gt;

&lt;p&gt;Despite all that, I do really look forward to the next iteration of E2, because as improvements are made to the hypervisor, and Moores law hold true. It would be about 2 more years where it outright replaces the N1 series, as the "better choice".&lt;/p&gt;

&lt;p&gt;More importantly, what this technology opens up is a new possibility. Of a future instance type (E3 ?), where one could be paying for raw CPU / ram usage directly instead. For any VM workload. Making the previous optimization (preemptible, serverless) potentially obsolete. &lt;/p&gt;

&lt;p&gt;Giving even legacy application developers a "have your cake and eat it too moment", where they can take any existing workload, and with no additional application change, with the benefit of "preemptible" instances.&lt;/p&gt;

&lt;p&gt;If Google Cloud has not realized it yet, if done correctly they can make huge wins in enterprise sales, which they desperately need (aka the people still running 20+ year old software).&lt;/p&gt;

&lt;p&gt;Till then, I will wait for GCP or another cloud provider to make such a change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~ Happy Testing 🖖🚀&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  About Uilicious
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://uilicious.com"&gt;Uilicious&lt;/a&gt; is a simple and robust solution for automating UI testing for web applications. Writing test scripts to validate your web platforms, can be as easy as the script below.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Which will run tests like these ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://test.uilicious.com/test/public/7t74nVS828weKMtzGgJppF"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MttsDKQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c2kos152a1tkkdsfewkc.gif" alt="uilicious demo"&gt;Catfeeding: Uilicious testing inboxkittens XD&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uilicious.com/pricing.html"&gt;👊 Test your own web app, with a free trial on us today&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_o0KwDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7b306yol0fp6hzt3gnt6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_o0KwDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7b306yol0fp6hzt3gnt6.jpg" alt="Skeptical Baby"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  (personal opinion rant) On GCP lacking the courage to leverage on E2 tech 😡
&lt;/h1&gt;

&lt;p&gt;The whole purpose of E2 is to create a new dynamic migratable workload&lt;/p&gt;

&lt;p&gt;So why is there even a preemptible option which makes no sense in almost any scenario when compared to other preemptible options? &lt;/p&gt;

&lt;p&gt;Also isn't the very point of E2 series designed to help for long low-CPU usage workload, why does your pricing structure not favor it?&lt;/p&gt;

&lt;p&gt;These are just the tip of the iceberg on Google confusing launched messaging.&lt;/p&gt;

&lt;p&gt;If GCP removed preemptible discount option and made this new lower-performing line of VM's 30% cheaper, where it sits nicely between preemptible N1 workload, and higher performing sustained N2 workload. &lt;/p&gt;

&lt;p&gt;It would have become a serious consideration and contender. However, without doing so, it's just cool tech, desperately trying to find a use case.&lt;/p&gt;

&lt;p&gt;Sadly, and frankly, the only reason I would see why GCP is reluctant to do price cut for a new instance type is that they either&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;legitimately fear a massive price war with Amazon (which is famously known to be willing to out-bleed their competitors), &lt;/li&gt;
&lt;li&gt;fear the new lower-priced product will eat into their revenue from existing customers. &lt;/li&gt;
&lt;li&gt;Or worse, they just didn't think of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Considering that this is the same company that made &lt;a href="https://cloud.google.com/appengine/"&gt;serverless tech in 2008&lt;/a&gt;, way ahead of any of their competitors, and not capitalize on it. There is a good chance it's the last option (deja vu?)&lt;/p&gt;

&lt;p&gt;All of this is disappointing for me to see, considering the massive amount of R&amp;amp;D and engineering resources went into making this happen. Which is a very classic google problem, really strong tech, that is disconnected from their business goals and users.&lt;/p&gt;

&lt;p&gt;Finally GCP please stop abusing the phrase "TCO", or "total cost of ownership" - we sysadmin and infrastructure personal have the tendency to think in terms of months or years (you know the server's entire potential lifespan and total cost of ownership) ... Some of us actually find it &lt;del&gt;insulting&lt;/del&gt; confusing when the term was used to imply savings when comparing to the existing long-running workload. When you actually meant to compare extremely short-lived workload instead. &lt;/p&gt;

&lt;p&gt;We actually calculate server expenses, and such misleading marketing just leads us into wasted time and effort evaluating our options and making an article on it in the process.&lt;/p&gt;

&lt;p&gt;~ Peace&lt;/p&gt;

</description>
      <category>devops</category>
      <category>serverless</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <item>
      <title>So meetup.com is going to charge attendees in future - what's next for event organizers?</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 15 Oct 2019 18:48:16 +0000</pubDate>
      <link>https://forem.com/picocreator/so-meetup-com-is-going-to-charge-attendees-in-future-what-s-next-for-event-organizers-5bme</link>
      <guid>https://forem.com/picocreator/so-meetup-com-is-going-to-charge-attendees-in-future-what-s-next-for-event-organizers-5bme</guid>
      <description>&lt;h1&gt;
  
  
  Context.
&lt;/h1&gt;

&lt;p&gt;Meetup.com was testing a new pricing model, where the attendee pays a $2 fee. The backlash was predictable, especially those organizing free events. And they have since reverted on it and started damage control.&lt;/p&gt;

&lt;p&gt;For the original announcement see: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/lp/paymentchanges"&gt;https://www.meetup.com/lp/paymentchanges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.archive.org/web/20191014205500/https://www.meetup.com/lp/paymentchanges"&gt;internet archive, in case it gets taken down&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Discussion?
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Should the tech community start looking elsewhere? If so, is there really a good alternative now?&lt;/li&gt;
&lt;li&gt;Is dev.to considering stepping in?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that this has to be viewed from different angles.&lt;/p&gt;

&lt;p&gt;For example, for a small new meetup (&amp;lt;10 attendees), not organizing on meetup.com could kill it.&lt;/p&gt;

&lt;p&gt;For established ones, alternatives are viable. But limits their network effects. So it's now a pros/cons based on cost.&lt;/p&gt;

&lt;p&gt;What are your thoughts on it?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>How do you manage deployment configs? (Especially large scale cloud agnostic ones)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 26 Jun 2019 09:40:42 +0000</pubDate>
      <link>https://forem.com/uilicious/how-do-you-manage-your-deployment-configs-especially-large-scale-cloud-agnostic-ones-358d</link>
      <guid>https://forem.com/uilicious/how-do-you-manage-your-deployment-configs-especially-large-scale-cloud-agnostic-ones-358d</guid>
      <description>&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;

&lt;p&gt;For the past few weeks, I have been using a crazy mix of the following. All within a git repository (infrastructure as code), as we migrate most of our infrastructure and workload from one provider to another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;terraform&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;bashscripts&lt;/li&gt;
&lt;li&gt;kubernetes yaml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With lots and lots of json, and yaml configuration files. Mostly generated from one system, to be piped to another (about 10k lines worth).&lt;/p&gt;




&lt;p&gt;&lt;code&gt;terraform&lt;/code&gt; which is commonly sold as a single solution for everything (it isnt), rapidly broke apart for us once we started doing deployments outside the usual AWS / GCP, and its poor support for kubernetes (which we use heavily)&lt;/p&gt;

&lt;p&gt;So we started patching up missing orchestration with nodejs + bashscripts.&lt;/p&gt;

&lt;p&gt;And now we have a giant soup of scripts updating other scripts configuration and applying them. Not exactly the most "elegant" solution.&lt;/p&gt;




&lt;h1&gt;
  
  
  The question : How do you do it?
&lt;/h1&gt;

&lt;p&gt;So wondering out loud, for those who do really large scale deployments, especially with a small team. &lt;/p&gt;

&lt;p&gt;Is it normal to always throw in the towel at the end, and code up a custom configuration management script to handle all this chaos? If so do you all normally do it in bash? or a custom application (like java)? or some other CLI scripting language.&lt;/p&gt;

&lt;p&gt;Alternatively, is it normal to just grow a really large sysadmin team, each managing a subset of the system?&lt;/p&gt;

&lt;p&gt;It feels like I am reinventing the wheel on these things, yet I somehow feel like there would have been a solution out there for this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sidetrack: a large part of me just feels like redoing terraform in nodejs out of frustration, to support my use cases.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Clarification on scale
&lt;/h1&gt;

&lt;p&gt;I do believe there are multiple cloud specific offerings out there, the reason we do not use any of them is currently we run on the following list of providers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gcp&lt;/li&gt;
&lt;li&gt;aws&lt;/li&gt;
&lt;li&gt;digital ocean&lt;/li&gt;
&lt;li&gt;linode&lt;/li&gt;
&lt;li&gt;alicloud&lt;/li&gt;
&lt;li&gt;hetzner&lt;/li&gt;
&lt;li&gt;bare metal on-premise stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Covering the following regions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;singapore&lt;/li&gt;
&lt;li&gt;unitedkingdom&lt;/li&gt;
&lt;li&gt;germany-frankfurt&lt;/li&gt;
&lt;li&gt;india-bangalore&lt;/li&gt;
&lt;li&gt;canada-toronto&lt;/li&gt;
&lt;li&gt;usa-newyork&lt;/li&gt;
&lt;li&gt;usa-sanfranc&lt;/li&gt;
&lt;li&gt;netherlands-amsterdam&lt;/li&gt;
&lt;li&gt;indonesia-jarkata&lt;/li&gt;
&lt;li&gt;hongkong&lt;/li&gt;
&lt;li&gt;taiwan&lt;/li&gt;
&lt;li&gt;+3 other data centers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complex web of providers comes in part from the need to support regions where another provider either does not exist, or does poorly performance wise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All to run UI tests at &lt;a href="https://uilicious.com"&gt;https://uilicious.com&lt;/a&gt; !??!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>agile</category>
      <category>devops</category>
    </item>
    <item>
      <title>Are we "developers" gatekeeping "knowledge" from our juniors and peers? 🤦</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 05 Jun 2019 15:14:25 +0000</pubDate>
      <link>https://forem.com/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6</link>
      <guid>https://forem.com/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6</guid>
      <description>&lt;h1&gt;
  
  
  Background Context
&lt;/h1&gt;

&lt;p&gt;My startup co-founder recently wrote an article on what I previously considered "common knowledge", an article which went "viral". &lt;/p&gt;

&lt;p&gt;Unfortunately, as per the rules of the internet. Anything "viral" will draw a fair bit of criticism and even personal attacks, especially outside dev.to; Some which I saw hurt my co-founder personally, as we look through them.&lt;/p&gt;

&lt;p&gt;However, this is not about sexism, javascript, nor the internet rule (all which has valid problems worth highlighting separately).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is about "developers", and our role in gatekeeping "knowledge" like the following from others&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/shiling" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XxasGPxV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Fp32YymT--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/11766/4a059374-4b4f-4777-bf0a-8a0301476298.png" alt="shiling image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Javascript Array.push is 945x faster than Array.concat 🤯🤔&lt;/h2&gt;
      &lt;h3&gt;Shi Ling ・ May  2 '19 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webperf&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Personal attacks aside, what caught my attention, that drove me to write this article - was the following comment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Isn't it obvious that ________________ are you a _____, &lt;br&gt;
&lt;strong&gt;Never ever write such [ ] articles again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
Side note: As its not my intention to shame/attribute the original author. The quote is paraphrased to make it un-googleable and to remove expletives. To be clear, the exact wording is different



&lt;p&gt;It struck me not only because of how cruel it was at the end - but how the statement, was something I was guilty of saying as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SL: Really?! &lt;code&gt;push&lt;/code&gt; made such a big difference&lt;/li&gt;
&lt;li&gt;EU: Well &lt;code&gt;concat&lt;/code&gt; needs to create a new array, and copy everything&lt;/li&gt;
&lt;li&gt;SL: Doesn't the javascript jvm, automatically optimize such execution? Seeing that I do not use the old array&lt;/li&gt;
&lt;li&gt;EU: Well it doesn't, and unfortunately due to the spec ...&lt;/li&gt;
&lt;li&gt;SL: I should write an article on this&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU: Isn't it very obvious, I doubt it's worth writing&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SL: It wasn't obvious to me&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;EU: (in my head - I am surprised you didn't know)&lt;/li&gt;
&lt;li&gt;EU: Ok, it's worth a try then
SL stands for Shi Ling, my co-founder. While EU represents me, Eugene
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While it's much lighter in tone, the line of reasoning to not write the article is the same.&lt;/p&gt;

&lt;p&gt;In the position of seniority, even lightly brushing off of such content being written can have chilling effects on other junior developers and peers. Especially in a much larger organization. So while such statements may not have deterred Shi Ling (she wrote it in the end), it could however for the rest of my peers around me silence them.&lt;/p&gt;

&lt;p&gt;And was I wrong in my criticism!&lt;/p&gt;

&lt;p&gt;Looking at the number of positive retweets and comments on how "I need to rewrite my code", or "I never knew", occurred. It wasn't obvious, and it is an article worth writing.&lt;/p&gt;

&lt;p&gt;However, what hit me really hard - was when out of "frustration" or immaturity. I regrettably looked into the commenter's profile. &lt;/p&gt;

&lt;p&gt;I realized it wasn't just a simple careless statement by a young teenager. &lt;/p&gt;

&lt;p&gt;It was from a well respectable senior engineer, in charge of a large team of developers, with a similar background to me. To me, it was terrifying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I was looking into the mirror, of what could be me in the future&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P1O1wRxS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a21inheg22a2vblosf1z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P1O1wRxS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a21inheg22a2vblosf1z.jpg" alt="The evil in the mirror"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And on further digging around the various comments, he was not the only one...&lt;/p&gt;

&lt;p&gt;Which brings me to my next point...&lt;/p&gt;




&lt;h1&gt;
  
  
  PSA: What is common to you, may not be to another developer. So think twice before suppressing such content.
&lt;/h1&gt;

&lt;p&gt;As an industry, programming is relatively immature, of being less then 200 years old (starting from Ada Lovelace). The web industry is even younger at being under 30 years old.&lt;/p&gt;

&lt;p&gt;This pales drastically to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scientific medicine: ~2500 years old&lt;/li&gt;
&lt;li&gt;architecture and construction: &amp;gt;5000 years old&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, the technology we learn seems to be constantly changing, with little in the way of a stable (think 12+ years) long term standard for anything.&lt;/p&gt;

&lt;p&gt;By this definition: any assumptions you made on what is learned for your generation as "common knowledge" that isn't worth spreading, is a false assumption made to your junior and peers.&lt;/p&gt;

&lt;p&gt;We should also kick out the notation that, if one does not "understand this", that individual is not deserving to be called a developer. &lt;/p&gt;

&lt;p&gt;To clarify, my co-founder is by no means a "junior" developer. We both have very very different specializations. &lt;/p&gt;

&lt;p&gt;While I may have really deep dive knowledge on the ES5 spec, and how V8 works internally, which distorted my view on what's considered "common knowledge" for javascript.&lt;/p&gt;

&lt;p&gt;Inversely, she has really deep encyclopedia-like knowledge on the various quirks of CSS, and how modern front end frameworks like &lt;code&gt;vue.js&lt;/code&gt; works. Something which I admittedly still make very fundamental novice mistakes till today.&lt;/p&gt;

&lt;p&gt;So if my assumptions are wrong to a fellow "senior" developer with different specialization, wouldn't it be worse for a new "junior" developer. Especially with the rise of coding boot camps.&lt;/p&gt;

&lt;p&gt;So until we can figure out a common stable universal engineering standard for programming, which we can assume as "common knowledge", like how architects or doctors do (which we probably will not for another 50 years). I am heavily ringing this bell now, as we are like no other industry out there.&lt;/p&gt;

&lt;p&gt;Never make that assumption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MGWaYUjr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0t9nzpa812aflac0rq5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MGWaYUjr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0t9nzpa812aflac0rq5.jpg" alt="Heraclitus quote: There is nothing permanent except change"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heck, to the unconvinced old seniors. Many of us (including me) used to consider C style memory pointer manipulation as essential common knowledge, which in absence, we used as grounds for a "no hire" for juniors.&lt;/p&gt;

&lt;p&gt;How wrong are we now in modern web development and the programming language they use?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Bro-grammer? Couldn't we be kinder?
&lt;/h1&gt;

&lt;p&gt;Even with valid criticism...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask yourself, would you have possibly made the same mistake?&lt;/li&gt;
&lt;li&gt;Would you like to have such things said to you, or your loved ones?&lt;/li&gt;
&lt;li&gt;Is it constructive criticism?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of gatekeeping what should be written or not written on "superior grounds". We could instead encourage improvement with our criticism.&lt;/p&gt;

&lt;p&gt;For example, one repeated criticism. Is regarding benchmarking methodology. However instead of statements like.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Worst benchmarking ever&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A kinder, more constructive comment would be&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could improve the benchmark by doing ____ instead of ____&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And to say it upfront, for most of the constructive criticism on benchmarking, you were right. While it probably will not change the end result, due to the large difference in between. It is a valid area of improvement, and both my co-founder and I do thank you for that.&lt;/p&gt;

&lt;p&gt;Or as XKCD lovingly put it&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5TYtxE_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ha6olaye4p1bhxumo6qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5TYtxE_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ha6olaye4p1bhxumo6qa.png" alt="XKCD #1053"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saying 'what kind of an idiot doesn't know about the Yellowstone supervolcano' is so much more boring than telling someone about the Yellowstone supervolcano for the first time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;So which would you rather be?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Bro-grammer?
&lt;/h1&gt;

&lt;p&gt;When I said this is not about sexism, I mean it.&lt;/p&gt;

&lt;p&gt;The suppression of knowledge probably happens regardless of gender. It definitely happens from senior to junior as well.&lt;/p&gt;

&lt;p&gt;However, the phrase "Bro-grammer" is used intentionally. &lt;/p&gt;

&lt;p&gt;I am sure there are exceptions (there always is), but this is more on us the majority, the male gender of the development community, then the other way.&lt;/p&gt;

&lt;p&gt;And while I will not have any statistical numbers to back it up. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The minority groups within our community know very well how it feels like to have their voice heavily suppressed.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The minority groups are the ones most mindful on not gatekeeping "knowledge" from others.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For almost all the personal attacks / mean criticism I seen to my co-founder was from our side of the male development community.&lt;/p&gt;

&lt;p&gt;And it's on us to improve ourselves on this, to improve our community. Consciously, in speaking out against it. Or sub-consciously in our words and actions.&lt;/p&gt;

&lt;p&gt;I am not asking each one of us to achieve an unreasonable perfectionist standard (as there will be slips of tounges), nor am I criticizing of who you are now. &lt;/p&gt;

&lt;p&gt;I am saying this, as someone who has made mistakes as well. To make small incremental personal, mindful changes in us individuals that will allow us to push the community forward.&lt;/p&gt;




&lt;h1&gt;
  
  
  So ask yourself the man in the mirror? Will you make that change?
&lt;/h1&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PivWY9wn5ps"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To improve the community around you. In encouraging the sharing of knowledge (as trivial as it may seem to you, or even me). Or to share such knowledge yourself.&lt;/p&gt;




&lt;h1&gt;
  
  
  I have seen similar things happen to other articles/tweets, anything I can do to help?
&lt;/h1&gt;

&lt;p&gt;Instead of fighting head on, and "feeding the trolls".&lt;/p&gt;

&lt;p&gt;In cases of unconstructive criticism (eg. worse benchmark), in place of the author, asked politely &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do you mean by _____, how could the content be improved on?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This gives the individual, a chance to clarify as they may not always mean harm intentionally, sometimes it could simply be words being lost in translation.&lt;/p&gt;

&lt;p&gt;Just keep in mind to remain &lt;strong&gt;respectful, constructive and clear&lt;/strong&gt; to defuse any tension&lt;/p&gt;

&lt;p&gt;For really harsh comments or personal attacks, it might be better to reply to the comment, or author, while ignoring the bully. To let them know that there are others who appreciated their work. While lightly pointing at the criticism.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personally I found the content useful, regardless of what anyone else says. Thank you&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above is universal regardless of content type (it can be applied outside of the development community).&lt;/p&gt;

&lt;p&gt;And finally, for the developer who engages in such criticism, and partly the reason I wrote this article - is so I can point to it in the future for others. As it happens to them. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personally I think what you are doing is great, regardless of what anyone else says. Thank you. And for the developers who say otherwise, we should be encouraging instead of suppressing our peers.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/picocreator" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--odgNY3b7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--lnJT1GdY--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/56734/7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg" alt="picocreator image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Are we "developers" gatekeeping "knowledge" from our juniors and peers? 🤦&lt;/h2&gt;
      &lt;h3&gt;Eugene Cheah ・ Jun  5 '19 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#motivation&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;p&gt;Keep in mind while doing any of the above, as you run the risk of diverting the critical comment from the author and onto yourself. If it gets personal on you, do not feed into it. Be strong and ignore it.&lt;/p&gt;

&lt;p&gt;Know you have done a part in letting the content creator, know that the criticism is not the only voice there. And as an observer to the receiving end, even if they end up in silence, I will say it helps go a long way in knowing there was someone there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For all the unsung heroes, who did so. Thank you.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Keep doing your best!
&lt;/h1&gt;

&lt;p&gt;To my co-founder, even if you face a double standard in your work previously, or the triple standards in investors as you fundraise presently, or the cruelty of the internet as you write content.&lt;/p&gt;

&lt;p&gt;To all the other working moms, single moms, LGBTQ, and other minority groups.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep on coding &lt;/li&gt;
&lt;li&gt;keep on improving&lt;/li&gt;
&lt;li&gt;keep on writing&lt;/li&gt;
&lt;li&gt;keep on doing, the various great things you are working on&lt;/li&gt;
&lt;li&gt;do not let the criticism suppress you&lt;/li&gt;
&lt;li&gt;push that glass ceiling&lt;/li&gt;
&lt;li&gt;and be great at what you do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Peace 🖖&lt;/p&gt;




&lt;h1&gt;
  
  
  Additional advice from comments
&lt;/h1&gt;

&lt;p&gt;Also, consider reading up on &lt;a href="https://www.recurse.com/social-rules"&gt;recurse.com/social-rules&lt;/a&gt;. &lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/downey"&gt;
        &lt;img class="profile-pic" src="https://res.cloudinary.com/practicaldev/image/fetch/s--QFJhW40M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--LTSCyk9P--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_50%2Cq_auto%2Cw_50/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/170552/797597da-2c9d-4fae-9ca6-b56639e40665.jpeg" alt="downey profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/downey"&gt;
        &lt;span class="comment-username"&gt;Tim Downey&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2"&gt;•&lt;/span&gt;
&lt;a href="https://dev.to/downey/comment/bg1d" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time&gt;
    Jun  6 '19
  &lt;/time&gt;
&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;Thanks for writing up this post &lt;a class="comment-mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;
!&lt;/p&gt;

&lt;p&gt;Along these lines, I am a big fan of the Recurse Center's &lt;a href="https://www.recurse.com/social-rules"&gt;social rules&lt;/a&gt; -- particularly in this case the one about "no feigning surprise." Their explanation of this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Feigned surprise is when you act surprised when someone doesn’t know something. Responding with surprise in this situation makes people feel bad for not knowing things and less likely to ask questions in the future, which makes it harder for them to learn.&lt;/p&gt;

&lt;p&gt;No feigning surprise isn’t a great name. When someone acts surprised when you don’t know something, it doesn’t matter whether they’re pretending to be surprised or actually surprised. The effect is the same: the next time you have a question, you’re more likely to keep your mouth shut. An accurate name for this rule would be no acting surprised when someone doesn’t know something, but it’s a mouthful, and at this point, the current name has stuck.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Feigning (or actually being) surprised is definitely not as mean spirited as some of the examples you referenced, but it can still discourage folks that are new to an area from speaking up or reaching out for help. Just wanted to note this as well since I think it's pretty easy for (otherwise well-intentioned) people to do this without meaning to.&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;And understand the difference between passive/active gate-keeping&lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/kayis"&gt;
        &lt;img class="profile-pic" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ua43TEXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Dya1ZKgv--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_50%2Cq_auto%2Cw_50/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/1106/c30ab362-496a-44c4-bcf5-0d4747afd39e.jpg" alt="kayis profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/kayis"&gt;
        &lt;span class="comment-username"&gt;K&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2"&gt;•&lt;/span&gt;
&lt;a href="https://dev.to/kayis/comment/bfjj" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time&gt;
    Jun  5 '19
  &lt;/time&gt;
&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;I think the problem is two-fold.&lt;/p&gt;

&lt;p&gt;One part is the active gate-keeping. People running around telling everyone they or the tools they use aren't good enough.&lt;/p&gt;

&lt;p&gt;How can you use PHP when there is Java? How Java when there is C#? Why switch to JavaScript when PHP finally became a &lt;em&gt;real&lt;/em&gt; programming language?&lt;/p&gt;

&lt;p&gt;Even my programming techniques professor said JavaScript was a toy and nothing compared to Java.&lt;/p&gt;

&lt;p&gt;People shitting on each others' work for all kind of senseless reasons instead of helping each other out. That needs to stop and I have the feeling that more women and minorities in the industry will just lead to that. CoCs are spreading like a wildfire and people become generally kinder. The few old-school uber-nerds I saw here on dev.to didn't stay for long.&lt;/p&gt;

&lt;p&gt;On the other hand, we have passive gate-keeping or simply ignorance. I guess that is what you're talking about. This can have many reasons and I can only talk about the ones I encountered.&lt;/p&gt;

&lt;p&gt;I often had the impression I'm a bad-to-mediocre developer, so I didn't value most of the stuff I knew and this lead me to think I was one of the last in the industry to learn stuff. Took me a year to understand functions, took me a few months to understand closures, took me another year to understand monads, etc. I was always a slow learner, swimming with 9, biking with 14, so I guess that's where the impression came from that everyone else already knew the stuff I just learned.&lt;/p&gt;

&lt;p&gt;I also had to work with many of such old-school uber-nerds who choose tech for projects and generally acted like they had it all figured out. Took me a few years of freelancing and blogging to find out most of them are just talking big and there are a whole bunch of people out there who don't know half as much as I do and would love to work with or learn from me.&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;





&lt;h1&gt;
  
  
  History sidetrack
&lt;/h1&gt;

&lt;p&gt;For those interested in the accuracy of the yearly figures used above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;1991 : HTTP protocol 0.9 was formalised&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Portable_computer"&gt;1975 : The rise of portable computer such as SCAMP / IBM 5100&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1958 : &lt;a href="https://en.wikipedia.org/wiki/Fortran"&gt;Fortran&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)"&gt;LISP&lt;/a&gt;, the predecessor to most modern programming languages.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/History_of_programming_languages"&gt;1842 : Ada Lovelace, published the first known computer program&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Herophilos"&gt;300 BC : Herophilos, the first greek anatomist to push scientific based medicine&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Megalithic_Temples_of_Malta"&gt;3600 BC : Megalithic Temples of Malta, the oldest known free-standing structure on earth, ie not carved into a mountain&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;I have been sitting on this piece for some time, it is intentionally written in a way to not blame individuals for their past action, but to encourage them to reflect back on how to do better.&lt;/p&gt;

&lt;p&gt;As writing is hardly my biggest strength, and I am sure my approach to this topic may be flawed as well. Any discussion, for similar experiences on this issue, either to encourage others, or to suggest other possible ideas, or even amendments that should be made to this article. Will be gladly appreciated.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>motivation</category>
    </item>
    <item>
      <title>⚙️ Explain Selenium &amp; Webdrivers automation (Like I'm Five)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 15 May 2019 07:42:31 +0000</pubDate>
      <link>https://forem.com/uilicious/explain-selenium-webdrivers-like-i-m-five-16ng</link>
      <guid>https://forem.com/uilicious/explain-selenium-webdrivers-like-i-m-five-16ng</guid>
      <description>&lt;p&gt;This was originally drafted as an answer to the question ...&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/edwinthinks" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F141569%2F9a509ae3-6266-4deb-a28c-714067d75ac2.jpeg" alt="edwinthinks"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/edwinthinks/explain-selenium-webdrivers-like-i-m-five-24me" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Explain Selenium &amp;amp; Webdrivers Like I'm Five&lt;/h2&gt;
      &lt;h3&gt;Edwin Mak ・ May 12 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#explainlikeimfive&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#selenium&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#e2e&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;That grew into an entire post on its own 😅 (I talk too much)&lt;/p&gt;

&lt;p&gt;As I'm the co-founder of &lt;a href="https://uilicious.com/" rel="noopener noreferrer"&gt;Uilicious.com&lt;/a&gt; - a full end-to-end testing platform built on webdriver servers. This post is meant to be a series of key facts I wish I knew at the start of the "webdriver" learning journey.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Oneliner to explaining Selenium / Webdriver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Automation standard, to control all your web browsers via an API.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Webdriver Specification vs Selenium Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think along the lines of HTML5 vs Chrome&lt;/li&gt;
&lt;li&gt;Webdriver refers to the specification&lt;/li&gt;
&lt;li&gt;Selenium refers to an opensource implementation of the specification. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selenium is currently the most prominent implementation of webdriver, with several additional commands in selenium that are not part of the webdriver specification. Causing confusion on the distinction in between.&lt;/p&gt;

&lt;p&gt;This is particularly important, to debug discrepancies in both documentation, for every browser. &lt;/p&gt;

&lt;p&gt;However as they are used extremely interchangeably, it can get incredibly confusing in guides and tutorials. This is partially due to their mixed history together. &lt;/p&gt;

&lt;p&gt;Understanding this difference will probably save you hours of documentation pain down the line.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Few9n9yw9l63bkk3xbd2g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Few9n9yw9l63bkk3xbd2g.jpg" alt="Every infant is a miracle"&gt;&lt;/a&gt;&lt;/p&gt;
Every child is a miracle



&lt;p&gt;&lt;strong&gt;Webdriver Specification is a mini-miracle on its own&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For those who have done work across multiple browsers, especially in the earlier years (10+ years ago). You will know how much of an impossibility it was to get a group of browser vendors, who are competitors to each other, agree on something together.&lt;/p&gt;

&lt;p&gt;The problem for webdriver, is that it's made worse by being a relatively "low priority" item, with resource constraints. Being a feature that will never be experienced directly by the end users. &lt;/p&gt;

&lt;p&gt;With every browser only having a single to a handful of developers working on their webdriver implementation.&lt;/p&gt;

&lt;p&gt;The fact that we have a somewhat working protocol alone, in w3c, is a small miracle from the many years of hard work from the Selenium development team.&lt;/p&gt;

&lt;p&gt;It may be at times incomplete and inconsistent among browsers, however, it is a spec that we can at least somewhat agree on, and be proud of since it started formalizing in 2011.&lt;/p&gt;

&lt;p&gt;So a small round of applause for them 👏&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I may be deeply critical, I do however greatly respect what they have done so far.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F74vt3vwrj5kiqpqvszlq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F74vt3vwrj5kiqpqvszlq.jpg" alt="Choose a flavour"&gt;&lt;/a&gt;&lt;/p&gt;
Choose any flavour



&lt;p&gt;&lt;strong&gt;Webdriver is a HTTP API, with many client-side library implementations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can choose your language/poison of choice here&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP API: &lt;a href="https://w3c.github.io/webdriver/" rel="noopener noreferrer"&gt;W3C Specification&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Javascript: &lt;a href="https://webdriver.io/" rel="noopener noreferrer"&gt;webdriver.io&lt;/a&gt; (this is confusing as they use the same name)&lt;/li&gt;
&lt;li&gt;PHP: &lt;a href="https://github.com/facebook/php-webdriver" rel="noopener noreferrer"&gt;php-webdriver&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ruby: &lt;a href="https://metacpan.org/pod/Selenium::Remote::Driver" rel="noopener noreferrer"&gt;Selenium::Remote::Driver&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One side advice, like the early days of CSS/JS on multiple browsers, learn to distrust the specifications compatibility between different browsers/clients. Documentation is inconsistent and all over the place.&lt;/p&gt;

&lt;p&gt;While there is &lt;a href="https://developer.mozilla.org/en-US/" rel="noopener noreferrer"&gt;MDN mozilla.org&lt;/a&gt; that will happily tell you what works (or not) on which browsers, for CSS/JS.&lt;/p&gt;

&lt;p&gt;Webdriver as of now do not have an equivalent online resource. Internally this is something we may plan to spin out on our own in Uilicious, considering we are starting to pile up a mountain of random post-it notes on how the protocol differs for each browser.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Webdriver is a "Remote Control Interface"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The phrase "Remote Control Interface" is the exact quote taken from the specification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Webdriver is built as an automation protocol, making it less than ideal for test automation as they have very different objectives. For example, webdriver has very limited assertion commands.&lt;/p&gt;

&lt;p&gt;To use webdriver for testing, most users will use a testing solution, which extends the commands for end-to-end testing.&lt;/p&gt;

&lt;p&gt;For example, to check if a text exists on screen in webdriver, you will need to run the following commands.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find the element where you expect the text to be&lt;/li&gt;
&lt;li&gt;get the element and its inner HTML text string&lt;/li&gt;
&lt;li&gt;check if text exists in the result text string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For uilicious (and a few other test solutions) we simplify it as a single command&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;I.see("Text You Expect to see on screen")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1idfrhdqzixqm9hi3luv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1idfrhdqzixqm9hi3luv.jpg" alt="Mac mini farm"&gt;&lt;/a&gt;&lt;/p&gt;
Need to run an army of safari tests? You may need one of these



&lt;p&gt;&lt;strong&gt;Webdriver Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://www.npmjs.com/package/selenium-standalone" rel="noopener noreferrer"&gt;Selenium-standalone&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typically a webdriver server refers to a single VM / Physical instance of the desired OS of choice, with the "webdriver server" program installed. &lt;/p&gt;

&lt;p&gt;Alternatively, this could include mobile devices attached to the webdriver serve, typically by a USB cable.&lt;/p&gt;

&lt;p&gt;This will then run typically up to 1 session at a time, on a single browser. Where the automation will take place from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webdriver Grid/Router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://www.seleniumhq.org/docs/07_selenium_grid.jsp" rel="noopener noreferrer"&gt;Selenium-grid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A dedicated server program, which connects multiple webdriver servers together for use on a single API endpoint. This is used to get around the limitations that each server will have, and run multiple sessions on a larger scale. &lt;/p&gt;

&lt;p&gt;Along with user authentication, and limitations for access to the grid (to prevent any one user from running too many tests at a time)&lt;/p&gt;

&lt;p&gt;Cloud providers for webdriver, Saucelab and browserstack provides such endpoints with devices as old as blackberries. Alongside the latest nightly build of chrome.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc268hibnopp6hwd3a4pw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc268hibnopp6hwd3a4pw.jpg" alt="Can you hear me from the other side?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Networking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One common analogy we use in the office, is to think of uilicious commands as instructions we give over the phone. To the operator on the other side being the webdriver server. Executing commands such as &lt;code&gt;I.goTo&lt;/code&gt;, &lt;code&gt;I.see&lt;/code&gt;, and &lt;code&gt;I.click&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So from a networking endpoint, to test any website - the webdriver server on the other side must be able to load the given testing URL.&lt;/p&gt;

&lt;p&gt;Likewise, just as how someone on the other side of the phone is unable to connect to a website hosted on your laptop on localhost directly. Neither can the webdriver server.&lt;/p&gt;

&lt;p&gt;However in a similar situation, you are able to provide access through the usage of other tools such as &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, or port forwarding.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Hopefully that's enough key pointers for anyone new to webdriver who wish to dive deeply into it 🙂&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  So about uilicious, which is built on webdriver?
&lt;/h1&gt;

&lt;p&gt;What I work on in uilicious, is to run test scripts like these&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lets go to dev.to&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Fill up search&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search&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;uilicious&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// I should see myself or my co-founder&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shi Ling&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eugene Cheah&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And churn out &lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE" rel="noopener noreferrer"&gt;sharable test result&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnreaiqlqnokx20xn8zym.gif" alt="Uilicious Snippet dev.to test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like the above. All through a fully managed cloud platform. &lt;a href="https://uilicious.com" rel="noopener noreferrer"&gt;https://uilicious.com&lt;/a&gt;&lt;br&gt;
(Go give it a try 😉)&lt;/p&gt;




&lt;h1&gt;
  
  
  Happy Shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>explainlikeimfive</category>
      <category>selenium</category>
      <category>e2e</category>
      <category>beginners</category>
    </item>
    <item>
      <title>😼 KittenRouter: Opensource 🔍 Elasticsearch Monitoring, Logging &amp; 🚦 Failover of webtraffic (with ☁️ Cloudflare Workers)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 14 May 2019 14:11:24 +0000</pubDate>
      <link>https://forem.com/uilicious/elasticsearch-monitoring-logging-failover-of-webtraffic-using-opensource-kittenrouter-with-cloudflare-workers-4bf3</link>
      <guid>https://forem.com/uilicious/elasticsearch-monitoring-logging-failover-of-webtraffic-using-opensource-kittenrouter-with-cloudflare-workers-4bf3</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1j3qpkan01o7c2n9ugg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1j3qpkan01o7c2n9ugg1.png" alt="KittenRouter logo"&gt;&lt;/a&gt;&lt;/p&gt;
Special thanks to Nai Jie for the logo (he also did InboxKitten logo)



&lt;h1&gt;
  
  
  Introducing KittenRouter - Serverless cousins to InboxKitten
&lt;/h1&gt;

&lt;p&gt;Since we were using Cloudflare serverless kittens for our API. Why not expand this for the UI assets as well?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log every web request on cloudflare (This is an &lt;a href="https://support.cloudflare.com/hc/en-us/articles/216672448-Cloudflare-Logs-formerly-ELS-" rel="noopener noreferrer"&gt;enterprise only feature&lt;/a&gt;), helping us to track server failures.&lt;/li&gt;
&lt;li&gt;Automated failover on server errors, to maintain overall site availability (High Availability!).&lt;/li&gt;
&lt;li&gt;All as a reusable class module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping the project small along the &lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy" rel="noopener noreferrer"&gt;"do one thing and do it well"&lt;/a&gt; concept, the pseudo code could be summarized as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;when receiving a request &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; each backend configuration &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;invalid backend configuration - wrong domain, etc&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;continue&lt;/span&gt; - to next backend configuration iteration
      &lt;span class="o"&gt;}&lt;/span&gt;
      perform cachable request to the backend
      log request to ElasticSearch backend - or alternative log provider
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; 
         request result is valid &lt;span class="o"&gt;||&lt;/span&gt; 
         is last configuration &lt;span class="o"&gt;||&lt;/span&gt; 
         default backend configuration
      &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return &lt;/span&gt;result - Ends request processing
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt; - to next backend configuration iteration
   &lt;span class="o"&gt;}&lt;/span&gt;      
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# PS: The above is conceptually the synchronous flow. In the actual&lt;/span&gt;
&lt;span class="c"&gt;# implementation, logging should occur asynchronously without blocking&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows us to configure redundancies within the backend, with commons.host servers coming in first, followed by firebase as "backup".&lt;/p&gt;


&lt;h1&gt;
  
  
  Background context (why we made this?)
&lt;/h1&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/uilicious" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F182%2F2f087e13-6b3f-4089-b781-a01543a38062.png" alt="Uilicious"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F56734%2F7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/why-we-migrated-serverless-inboxkitten-from-firebase-to-cloudflare-workers-and-commonshost-gd3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Why we migrated opensource 😼inboxkitten (77 million serverless request) from 🔥Firebase to ☁️Cloudflare workers &amp;amp; 🐑CommonsHost&lt;/h2&gt;
      &lt;h3&gt;Eugene Cheah for Uilicious ・ Mar 27 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#whywe&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;When we migrated our UI frontend from Firebase to &lt;a href="https://commons.host/" rel="noopener noreferrer"&gt;Commons.Host (an opensource and public CDN/Static hosting providers)&lt;/a&gt;, one of the issues we faced was random 404's in random remote locations. Like Russia - possibly due to the lack of servers there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpy4xba5ennrfjt3phh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpy4xba5ennrfjt3phh9.png" alt="Commonshost Global Map"&gt;&lt;/a&gt; &lt;/p&gt;
Commons.host, server locations at point of writing



&lt;p&gt;And because we are supporters of the growing 🇸🇬 Commons Host project, we decided to stand by them together, instead of ditching them for alternatives.&lt;/p&gt;

&lt;p&gt;We wanted a better way to debug such issues while improving the overall reliability of our service.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note to any big-brother authorities, we only track static assets loading with IP address masking on inboxkitten.com. No logging is done on the API. So yea, do not bother asking me for such data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Elasticsearch Monitoring: Commons Host
&lt;/h1&gt;

&lt;p&gt;What's awesome, is using the logging done by KittenRouter, of our commons host deployment, we can start having hard data on its status.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fui2rilz7vuv581xg1qm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fui2rilz7vuv581xg1qm8.png" alt="HTTP code logging"&gt;&lt;/a&gt;&lt;/p&gt;
78 errors out of 12,069 requests ~ 0.65% failure rate



&lt;p&gt;Is that currently in such a setup, we have 99.35% of our traffic routed through the free public servers of commons.host. &lt;/p&gt;

&lt;p&gt;And for the remaining 0.65% of request which fails, They automatically fall back onto firebase infrastructure, under its free tier.&lt;/p&gt;

&lt;p&gt;While it's not yet 9 nines, considering that these commons.host servers and cloudflare servers are scattered around the world. &lt;a href="https://www.cdnperf.com/#!rum" rel="noopener noreferrer"&gt;It's well within the 98% of real world user metrics of most CDNs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4f3n8js8c6mzd2vlhwgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4f3n8js8c6mzd2vlhwgb.png" alt="Commons host dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, with the header information provided, we can keep track of the error rate on a per server basis. And with the KittenRouter setup on Cloudflare, you can configure such monitoring for any existing site without application code change.&lt;/p&gt;

&lt;p&gt;We have since provided the commons.host team access to the above elasticsearch cluster for extracting useful data on their infrastructure under real user load.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clarification notes, for privacy reasons, commons.host do not perform such level of logging on their systems for sites hosted on their platform. &lt;/p&gt;

&lt;p&gt;The intention here for us at Uilicious, is to provide them with useful real-world usage metrics of their servers, and effectively exempting our website, inboxkitten.com, from such privacy protection, to help them improve their service.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Elasticsearch Monitoring: InboxKitten
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx54ehgmxsfkkicfke089.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx54ehgmxsfkkicfke089.png" alt="KittenRouter world map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we are able to easily use cloudflare country codes to help us keep track of our Kitten world domination map - live!. Meow-hahaha!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Greenland, and Madagascar - I got my &lt;del&gt;plague inc&lt;/del&gt; InboxKitten eyes set on you!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Cool! Where can I get the code
&lt;/h1&gt;

&lt;p&gt;For deployment and configuration details...&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/uilicious" rel="noopener noreferrer"&gt;
        uilicious
      &lt;/a&gt; / &lt;a href="https://github.com/uilicious/KittenRouter" rel="noopener noreferrer"&gt;
        KittenRouter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;What is KittenRouter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;KittenRouter is a routing script for &lt;a href="https://www.cloudflare.com/products/cloudflare-workers/" rel="nofollow noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; that attempts to connect to a list of specified servers and redirect the request to whichever server that is currently 'alive' at that point of time. It is extremely useful when you have servers that may go down or are unavailable to process the request and KittenRouter can automatically attempt to redirect the request to the next configured URL for processing.&lt;/p&gt;
&lt;p&gt;At the same time, it can be configured to log down information to your ElasticSearch server for analytical purposes. Some of the information logged are the status of the servers, country of the request and etc. For the full details, see the &lt;code&gt;index.js&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How to use KittenRouter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Ultimately, KittenRouter is used together with Cloudflare workers. There are two ways in which you can use KittenRouter on your Cloudflare worker script,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using NPM modules&lt;/li&gt;
&lt;li&gt;Adding KittenRouter manually&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1)&lt;/h3&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/uilicious/KittenRouter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://www.npmjs.com/package/kittenrouter" rel="noopener noreferrer"&gt;or alternatively NPM&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  That's cool, but Why do I even need Inboxkitten disposable email for again?
&lt;/h1&gt;

&lt;p&gt;One of the key use cases currently, and why we built this project, is to perform email validations as part of our automated test scripts. Such as the following...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Lets goto inbox kitten&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://inboxkitten.com&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open-Source Disposable Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Go to a random inbox inbox &lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SAMPLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Mail Nyow!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check that its empty&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There for no messages for this kitten :(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Testing for regular email&lt;/span&gt;
&lt;span class="c1"&gt;// (sent using a jenkins perodic build)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://inboxkitten.com&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open-Source Disposable Email&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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;ik-reciever-f7s1g28&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Mail Nyow!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// See an email we expect, nyow&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Testing inboxkitten subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With sharable test results such as &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/7t74nVS828weKMtzGgJppF" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrqyi0pohzc7v1is96g0.gif" alt="uilicious demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plus it's simple, cool, and fun to tinker around with.&lt;/p&gt;


&lt;h1&gt;
  
  
  What's next for KittenRouter?
&lt;/h1&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Very rough roadmap of improvements needed for KittenRouter
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#3&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/PicoCreator" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fu%2F17175484%3Fv%3D4" alt="PicoCreator avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/PicoCreator" rel="noopener noreferrer"&gt;PicoCreator&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;&lt;time&gt;May 10, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;What's next then? For KittenRouter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More configurable backend options
&lt;ul&gt;
&lt;li&gt;Request domain, path, region&lt;/li&gt;
&lt;li&gt;Request timeout&lt;/li&gt;
&lt;li&gt;Error / Passing HTTP code control&lt;/li&gt;
&lt;li&gt;Custom headers control&lt;/li&gt;
&lt;li&gt;Perform request as specified domain on specific IP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;More logging options
&lt;ul&gt;
&lt;li&gt;parameters logging whitelist/blacklist for
&lt;ul&gt;
&lt;li&gt;Headers&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;More logging providers
&lt;ul&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Let's see where this InboxKitten journey goes next... Till then we will be taking a cat nap!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfarhu4m4mo2d3is3nqz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfarhu4m4mo2d3is3nqz.jpg" alt="Cat sleeping"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Happy Shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>showdev</category>
      <category>serverless</category>
      <category>cloudflare</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🤔 UI framework wars &amp; testing survey (of Startups &amp; SME's in 🇸🇬 Singapore)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 23 Apr 2019 08:30:34 +0000</pubDate>
      <link>https://forem.com/uilicious/ui-framework-wars-testing-survey-results-on-startups-sme-s-in-singapore-1bj1</link>
      <guid>https://forem.com/uilicious/ui-framework-wars-testing-survey-results-on-startups-sme-s-in-singapore-1bj1</guid>
      <description>&lt;p&gt;Hey there,&lt;/p&gt;

&lt;p&gt;For the past month, the team of Uilicious has been going around one north (our country startup hub), and a few other business hubs, knocking over 200 doors to survey the folks and find out what's the most popular front end frameworks used in production and the state of testing in Singapore 🇸🇬&lt;/p&gt;

&lt;p&gt;Something that Uilicious as a company did on a much smaller scale 2 years ago, to get the general sense of things. And hopefully, plan to repeat for the next few years.&lt;/p&gt;

&lt;p&gt;🤔 Sounds good? let's get to the results&lt;/p&gt;




&lt;h1&gt;
  
  
  UI Framework Wars - in production?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;the endgame answer everyone wants to know on the ongoing UI "standard" war&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HDOUXdke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjppaejqiqho3oexe47l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HDOUXdke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjppaejqiqho3oexe47l.png" alt="XKCD 927 - standards"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vS2on42s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xlpqswtgrpw17etfubzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vS2on42s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xlpqswtgrpw17etfubzk.png" alt="UI Framework Wars"&gt;&lt;/a&gt;&lt;/p&gt;
Note that the total is more than 100%, as users are allowed to select multiple options



&lt;p&gt;React was hands down the winner 🔥 when it comes to applications developed in production.&lt;/p&gt;

&lt;p&gt;The emphasis on production is intentional. After all, with the recent changes and hype of newer frameworks it is important to remember that it is slow and tedious for production workload under daily usage and development to constantly stay on the hype train and takes a long time to migrate from one framework to another. With JQuery being a close 2nd place on the list or higher for easily the past 10 years 😴&lt;/p&gt;

&lt;p&gt;Another surprising contender, Ruby on Rails. Originally not an option among the list, it had such a significant amount of responders in the "Others" free text option, that it was added onto the list.&lt;/p&gt;

&lt;p&gt;Sad contenders for me personally, is Vue.js is not as popular as I hoped (disclosure, we are Vue.js supporters and help organize their meetups locally). &lt;/p&gt;

&lt;p&gt;Another perhaps more surprising result: 0 response for polymer web components, which was an option in the survey. Probably because it's a little too bleeding edge for production 🚅&lt;/p&gt;




&lt;h1&gt;
  
  
  Development Team Size?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mv_c_dEu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8uvzwkvj15aj9udogyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mv_c_dEu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8uvzwkvj15aj9udogyp.png" alt="Development Team Size"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of the teams surveyed, a good half has a development team size between 5-10. This is a good indicator that most companies have outgrown their initial startup days. &lt;/p&gt;

&lt;h1&gt;
  
  
  Who does testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V024aTDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jmeb3bbit290zpaibat9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V024aTDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jmeb3bbit290zpaibat9.png" alt="Who does testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interestingly, despite the labor crunch of programmers, most companies heavily rely (48.5%) on their developers for testing. &lt;/p&gt;

&lt;p&gt;This is worrying in two ways. One is that developers are typically not a good testers of their own code due to tunnel vision. Two, they are also the most costly option, with the average developer salary being easily double a dedicated manual QA salary 😶&lt;/p&gt;

&lt;p&gt;However what was even more shocking 😱 was that a significant 15% of respondent indicated that they do not test, and this included large dev teams that are &amp;gt;10. &lt;/p&gt;

&lt;p&gt;Followed by another 15%, who use their customer themselves, or their business stakeholders for testing.&lt;/p&gt;

&lt;p&gt;Although some indicated that this is not a problem, especially those who serve as a solution vendor, this will lead to an unhealthy adversarial relationship between the development team and the business team. &lt;br&gt;
Tension worsen when previously fixed bugs reappear through regression testing and the business team who in most cases, faces the wrath of the clients and the impact of any client business lost.&lt;/p&gt;

&lt;p&gt;Something that ideally a good QA team or even a single dedicated QA tester can help manage, which thankfully 20% of the respondents had. So a small win here 👍 &lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;Side note: The results below, excludes teams without a testing process.&lt;/p&gt;

&lt;p&gt;Unkown represent respondents who are not directly involved in the full testing process, and hence could not give an accurate answer &lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  How much time does your team spend on testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WCCKZlSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6f6ypjjo3ff0fj9fheki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WCCKZlSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6f6ypjjo3ff0fj9fheki.png" alt="Team testing time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While not shown in the chart, having a dedicated QA helps bring the overall development team time spent on testing downwards, with most in the 0-10%, and 10-25% category 😃&lt;/p&gt;

&lt;p&gt;Insufficient test coverage tends to be the main issue for teams who spend more than 25% of their time testing. Along with the practical time-consuming nature of manually performing testing on various devices and screen sizes ⌛&lt;/p&gt;

&lt;p&gt;This is further compounded if the developers are the testers, as it is not an effective use of their time.&lt;/p&gt;
&lt;h1&gt;
  
  
  How often do you test your application?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tk06-DJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u5gaiwzcs9980mlz7euu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tk06-DJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u5gaiwzcs9980mlz7euu.png" alt="Testing frequency"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A large 40% of teams seem to be running on a weekly or bi-weekly release cycle of development and testing.&lt;/p&gt;

&lt;p&gt;Followed by as and when deployments are done on an (unknown schedule), which is typically once a month or less. &lt;/p&gt;

&lt;p&gt;Finally a round of applause for the rare 3% who either automate their test to run every single workday or on every commit 😎&lt;/p&gt;
&lt;h1&gt;
  
  
  What tools do you use to test?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mL4W6f9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjg2cd7n6arj6lo5s2h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mL4W6f9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjg2cd7n6arj6lo5s2h5.png" alt="Testing tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite the whole growth of high tech web apps using the next buzz from AI to the blockchain. Testing is happening in rather low-tech ways, manually for all these apps 🤐&lt;/p&gt;

&lt;p&gt;One thing heartening to see is the rise of test automation at 27%, this is significantly higher then the ~5% result we saw two years ago. Perhaps indicative of a slowly maturing industry.&lt;/p&gt;


&lt;h1&gt;
  
  
  How often do you get a bug report from customers/business team?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hpI-tCeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7sn29pu2snehk45lxyoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hpI-tCeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7sn29pu2snehk45lxyoo.png" alt="Bug Frequency"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A particularly painful question for most. From what it seems, daily and weekly bug reports seem to be the daily grind faced by 75% of the teams.&lt;/p&gt;

&lt;p&gt;Something which almost every dev dread to get from a customer 😭&lt;/p&gt;
&lt;h1&gt;
  
  
  What is the biggest challenge for testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VVA95-gA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ktb1kbhcs453ijipiu0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VVA95-gA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ktb1kbhcs453ijipiu0t.png" alt="Challenges for testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, there is the open-ended question on what is the biggest challenge teams faced for testing.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, communication is still the number one challenge at 38%. A great shout out to &lt;a href="https://en.wikipedia.org/wiki/The_Mythical_Man-Month"&gt;The Mythical Man-Month&lt;/a&gt; for those who read it.&lt;/p&gt;

&lt;p&gt;Miscommunication of requirements ranges anywhere between any of the following: Customers, Business Team, Developers, Designers, Managers and Testers.&lt;/p&gt;

&lt;p&gt;This is followed by the lack of manpower in the current talent crunch (27%) and the scale of web testing various screen sizes, pages, and browsers (18%). All of which is something Uilicious as a company will be glad to help with.&lt;/p&gt;

&lt;p&gt;And finally the startup classic, no budget. At 12%, this never changes 😫&lt;/p&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q_MLC8i_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gxtdlkcf7zb7r7ft0pm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q_MLC8i_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gxtdlkcf7zb7r7ft0pm.jpg" alt="Hmm...."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The following are personal observations and opinions regarding the survey. AKA. it's not backed by hard data.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  Personal observations on testing
&lt;/h1&gt;

&lt;p&gt;What we found interesting was that most companies surveyed either have their entire development team or the majority of the team outsourced overseas, an indicator of the ongoing programmer manpower crunch in Singapore 🤔&lt;/p&gt;

&lt;p&gt;A worrying observation, from the various interview, was that most teams who test manually, only tend to do so for new features. Without any regression check. Or more scarily, those that do no testing.&lt;/p&gt;

&lt;p&gt;This is understandable for extremely early-stage startups, who just started. But, several were not in this stage, including larger "Series-A/B" level startups.&lt;/p&gt;

&lt;p&gt;This is worrying personally as a consumer of the modern web, as this would explain many times what has happened when extremely obvious bugs appear when using an application (such as login and checkout), where simple regression testing is missing.&lt;/p&gt;

&lt;p&gt;It also raises questions in me, if a company is not willing to invest any resources into testing functionality, what resources are in place to ensure the security of their system? Especially considering my own personal data on some of these applications.&lt;/p&gt;

&lt;p&gt;Something I hope will be improved on in time 😧&lt;/p&gt;
&lt;h1&gt;
  
  
  Personal observations on UI framework
&lt;/h1&gt;

&lt;p&gt;On the UI framework war side of things, many responders during the interview process mentioned plans to experiment and move away from React onto other frameworks for new projects or as part of their migration plan. So perhaps next year React will slowly topple down?&lt;/p&gt;
bias mode engaged



&lt;p&gt;&lt;strong&gt;Go Vue, go 🏎️&lt;/strong&gt;&lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;So how can Uilicious help make this better?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Uilicious.com testing platform
&lt;/h1&gt;

&lt;p&gt;First, we are a test automation platform aiming to remove all the pain points in website testing (hence the survey). And we are constantly iterating based on feedback.&lt;/p&gt;

&lt;p&gt;To get users up and running quickly, our platform allows testers to easily write test scripts like these ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lets go to dev.to&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;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Fill up search&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;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search&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;uilicious&lt;/span&gt;&lt;span class="dl"&gt;"&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;pressEnter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// I should see myself or my co-founder&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;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shi Ling&lt;/span&gt;&lt;span class="dl"&gt;"&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;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eugene Cheah&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And churn out &lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE"&gt;sharable test result&lt;/a&gt; like these ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5llcnkKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gyrtj5lk2b2bn89z7ra1.gif" alt="Uilicious Snippet dev.to test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Uilicious QA-as-a-service
&lt;/h1&gt;

&lt;p&gt;Second, we have since then soft launched &lt;a href="https://services.uilicious.com/qa-service"&gt;Uilicious Services&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As made obvious in the survey result. Most teams lack the resources required to form their own dedicated Quality Assurance team. And that is something we can help with, from gathering testing requirements to the writing and maintaining of tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://services.uilicious.com/qa-service"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yAlFPb4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zdxp8woa1ptynq7c57yq.png" alt="Uilicious Services"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the above testing challenges in the survey apply to you, let us know and we will be glad to help with our Uilicious testing service, to get your full QA process up and running with our experienced QA engineers. &lt;/p&gt;

&lt;p&gt;Also once the team grows large enough to have their own dedicated QA team, they can take over the role of writing and maintaining test scripts on the platform.&lt;/p&gt;

&lt;p&gt;We hope that through the testing service, we can help teams to convince their project managers on to improve the overall quality of their website and product 😉 and get dev teams up and running faster, with their projects, alongside our test platform. &lt;/p&gt;



bias mode dis-engaged



&lt;h1&gt;
  
  
  What should we improve on for next year survey?
&lt;/h1&gt;

&lt;p&gt;One notable thing: is that due to the current survey size (~200), and the nature of how the data is collected (almost entirely at one-north). There is a heavy bias towards smaller startups. So take the result as vague indicators at best. Something we hope to improve on in the future 😵&lt;/p&gt;

&lt;p&gt;Another thing to note - due to the nature of how this survey is conducted, the result greatly underrepresents the larger development teams.&lt;/p&gt;

&lt;p&gt;Many of the much larger startups (who probably have a larger development team) would have an office manager at the entrance, turning away any survey requests 😥&lt;/p&gt;

&lt;p&gt;When we repeat the survey next year, we will ask for both the local and overseas team size separately, which would give some insights on the rate of outsourcing in Singapore for developers.&lt;/p&gt;

&lt;p&gt;Similarly, there are clear obvious differences in testing requirements for solutions vendor, or a web company. Something we would try to add next year. Along with the question of "how old the company is".&lt;/p&gt;

&lt;p&gt;So if you have any ideas on how we should improve the survey, do let me know.&lt;/p&gt;

&lt;p&gt;Till then, as a startup founder myself. I gotta get back to investors and fundraising to grow Uilicious, so gotta go now...&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqnDCo3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/46wdha7noxft6xch4owu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqnDCo3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/46wdha7noxft6xch4owu.png" alt="Gotta Go"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Happy shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>testing</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
