<?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: Kevin Burns</title>
    <description>The latest articles on Forem by Kevin Burns (@kevburnsjr).</description>
    <link>https://forem.com/kevburnsjr</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%2F27158%2F9449bc13-1aa3-4703-ba8a-e23582c82d93.png</url>
      <title>Forem: Kevin Burns</title>
      <link>https://forem.com/kevburnsjr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kevburnsjr"/>
    <language>en</language>
    <item>
      <title>The Large Language Centipede</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Thu, 28 Aug 2025 15:10:09 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/the-large-language-centipede-1ogd</link>
      <guid>https://forem.com/kevburnsjr/the-large-language-centipede-1ogd</guid>
      <description>&lt;p&gt;First gather a corpus of text. Let's call it the entire public internet.&lt;/p&gt;

&lt;p&gt;Next train a &lt;em&gt;Large Language Model&lt;/em&gt; (LLM) against this corpus and you will get a system like Grok, Gemini or ChatGPT which approximates &lt;em&gt;Artificial Intelligence&lt;/em&gt; (AI).&lt;/p&gt;

&lt;p&gt;Now sell this LLM to people who use it to generate content they later post to the internet.&lt;/p&gt;




&lt;p&gt;The moment you make available the first generation of your LLM, you will immediately begin developing the second generation of your LLM in order to remain competitive in the AI arms race.&lt;/p&gt;




&lt;p&gt;Gather a new corpus of text. A fresh snapshot of the entire public internet.&lt;/p&gt;

&lt;p&gt;Train your new LLM against this new corpus, knowing that it contains some non-zero percentage of LLM generated content produced by users of the first generation of your product.&lt;/p&gt;

&lt;p&gt;Now sell v2 of your LLM to more people who use it to generate more content they later post to the internet.&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%2Fgjzfx55yljkj9q66trho.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%2Fgjzfx55yljkj9q66trho.png" alt="The Large Language Centipede" width="780" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see &lt;a href="https://en.wikipedia.org/wiki/The_Human_Centipede_(First_Sequence)" rel="noopener noreferrer"&gt;where this is going&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Each new generation of your LLM is trained on a higher percentage of LLM generated inputs as the last.&lt;/p&gt;

&lt;p&gt;This leads to &lt;em&gt;Macro self-training&lt;/em&gt; where large language models are inadvertently affecting the quality of successive generations by feeding their own outputs (and the outputs of other models) into the inputs of the newer generation of models.&lt;/p&gt;

&lt;p&gt;However inaccurate LLMs are today, we should expect them to become more deranged and hallucinatory over time. Not less.&lt;/p&gt;




&lt;p&gt;When developing the nuclear bomb, some scientists worried it might ignite the atmosphere and end the human race.&lt;/p&gt;

&lt;p&gt;This concern did not stop them from detonating the most destructive device ever constructed.&lt;/p&gt;

&lt;p&gt;If you think legitimate concerns regarding the safety of AI will be heeded, you don't understand the macroeconomics. These multi-billion dollar models are going to choke on their own excrement spraying trash across the entire internet and crash the global economy full speed into the brick wall that is &lt;em&gt;The Ouroboros Problem&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ouroboros</category>
    </item>
    <item>
      <title>Skipfilter</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Sat, 26 Mar 2022 22:56:27 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/skipfilter-2c54</link>
      <guid>https://forem.com/kevburnsjr/skipfilter-2c54</guid>
      <description>&lt;p&gt;A &lt;strong&gt;skipfilter&lt;/strong&gt; is a skip list of arbitrary elements that can be efficiently filtered using roaring bitmaps stored in an LRU cache.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kevburnsjr/skipfilter" rel="noopener noreferrer"&gt;https://github.com/kevburnsjr/skipfilter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use this data structure to make an open source pub/sub messaging system &lt;strong&gt;20,000x more efficient&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mercure.rocks/spec#topic-selectors" rel="noopener noreferrer"&gt;Mercure&lt;/a&gt; is a pub/sub message protocol for Server Sent Events. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Topics&lt;/em&gt; are identified by their URI&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Subscriptions&lt;/em&gt; are expressed as URI Templates (topic selectors)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;Each time a message is published to the hub, every topic selector in every subscription must be tested against every one of the message's topics. So if you have 5000 subscribers with 3 topic selectors and each message is published to 2 topics, that's 30,000 topic selector comparisons per message.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/dunglas/mercure" rel="noopener noreferrer"&gt;Mercure Hub&lt;/a&gt; originally dealt with this issue using an in memory cache to eliminate duplicate regular expression evaluations. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key (string): &lt;code&gt;${uri_template}-${topic_uri}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value (boolean): &lt;code&gt;regexp.Match(uri_template, topic_uri)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this worked well initially to improve latency, it still required 30,000 hash table lookups per message which did not scale well to large numbers of subscribers.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/dunglas/mercure/issues/558" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        [leak] High CPU usage with a lot of subscribers
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#558&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/divine" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F48183131%3Fv%3D4" alt="divine avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/divine" rel="noopener noreferrer"&gt;divine&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/dunglas/mercure/issues/558" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 03, 2021&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;This issue has been created to track down the problem that I'm having with Mercure:&lt;/p&gt;
&lt;p&gt;Mercure uses almost %100 CPU, doesn't matter what specs you've.&lt;/p&gt;
&lt;p&gt;The reason is probably a lot of subscribers and updates published to them:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# TYPE mercure_subscribers gauge
mercure_subscribers 4299
# HELP mercure_subscribers_total Total number of handled subscribers
# TYPE mercure_subscribers_total counter
mercure_subscribers_total 2.939146e+06
# HELP mercure_updates_total Total number of handled updates
# TYPE mercure_updates_total counter
mercure_updates_total 7.891176e+06
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just for 2 days, this is a reduced amount, normally it's up to x4 of updates that are currently disabled.&lt;/p&gt;
&lt;p&gt;Here is debug profile of CPU:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;File: caddy
Type: cpu
Time: Aug 26, 2021 at 6:53am
Duration: 300.16s, Total samples = 461.92s (153.89%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 231.78s, 50.18% of 461.92s total
Dropped 1620 nodes (cum &amp;lt;= 2.31s)
Showing top 10 nodes out of 141
      flat  flat%   sum%        cum   cum%
    55.77s 12.07% 12.07%    191.39s 41.43%  runtime.selectgo
    41.19s  8.92% 20.99%     52.73s 11.42%  runtime.sellock
    24.36s  5.27% 26.26%     24.40s  5.28%  runtime.(*waitq).dequeue (inline)
    22.55s  4.88% 31.15%    114.28s 24.74%  github.com/dunglas/mercure.(*Subscriber).CanDispatch
    18.83s  4.08% 35.22%     18.83s  4.08%  go.uber.org/zap.(*Logger).check
    16.90s  3.66% 38.88%     17.25s  3.73%  runtime.unlock2
    14.31s  3.10% 41.98%     32.20s  6.97%  github.com/dunglas/mercure.canReceive
    13.21s  2.86% 44.84%     13.21s  2.86%  runtime.empty
    12.48s  2.70% 47.54%     44.65s  9.67%  runtime.schedule
    12.18s  2.64% 50.18%     12.64s  2.74%  runtime.gopark
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/dunglas/mercure/files/7103058/pprof.caddy.samples.cpu.006.pb.gz" rel="noopener noreferrer"&gt;pprof.caddy.samples.cpu.006.pb.gz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is the image of it:
&lt;a href="https://user-images.githubusercontent.com/48183131/131874372-28094f39-e4e8-40dc-a31f-6dfe5c0bfcd3.png" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F48183131%2F131874372-28094f39-e4e8-40dc-a31f-6dfe5c0bfcd3.png" alt="dump1"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The problematic lines are:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dunglas/mercure/blob/ca2df66f27356aa2511ef4f959fc4156a0f69a94/subscriber.go#L68-L102" rel="noopener noreferrer"&gt;https://github.com/dunglas/mercure/blob/ca2df66f27356aa2511ef4f959fc4156a0f69a94/subscriber.go#L68-L102&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At first, we thought the problem is with &lt;code&gt;Zap(*Logger) check&lt;/code&gt; but PR &lt;a href="https://github.com/dunglas/mercure/pull/554" rel="noopener noreferrer"&gt;https://github.com/dunglas/mercure/pull/554&lt;/a&gt; should fix it.&lt;/p&gt;
&lt;p&gt;Anyways, completely removing Zap logging, the situation didn't improve but instead showed more problems with &lt;code&gt;runtime.selectgo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here is debug profile of CPU:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;File: caddy
Type: cpu
Time: Aug 31, 2021 at 12:27pm
Duration: 300.10s, Total samples = 568.67s (189.49%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 347.87s, 61.17% of 568.67s total
Dropped 1614 nodes (cum &amp;lt;= 2.84s)
Showing top 10 nodes out of 116
      flat  flat%   sum%        cum   cum%
    76.58s 13.47% 13.47%    263.92s 46.41%  runtime.selectgo
    50.10s  8.81% 22.28%     50.28s  8.84%  runtime.(*waitq).dequeue (inline)
    47.13s  8.29% 30.56%     59.44s 10.45%  runtime.sellock
    29.29s  5.15% 35.71%     29.29s  5.15%  memeqbody
    29.05s  5.11% 40.82%     86.37s 15.19%  github.com/dunglas/mercure.(*Subscriber).CanDispatch
    28.25s  4.97% 45.79%    166.05s 29.20%  github.com/dunglas/mercure.(*Subscriber).Dispatch
    25.76s  4.53% 50.32%     57.32s 10.08%  github.com/dunglas/mercure.canReceive
    25.14s  4.42% 54.74%     25.14s  4.42%  runtime.empty (inline)
    18.58s  3.27% 58.01%     19.21s  3.38%  runtime.gopark
    17.99s  3.16% 61.17%     17.99s  3.16%  runtime.(*gQueue).pop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the image of it:
&lt;a href="https://user-images.githubusercontent.com/48183131/131874958-c2aeb3cf-348e-41fa-9ebd-f9f2f9a9f60c.png" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F48183131%2F131874958-c2aeb3cf-348e-41fa-9ebd-f9f2f9a9f60c.png" alt="dump2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sorry, I can't write a benchmark for it but ready to test what changes is needed.&lt;/p&gt;
&lt;p&gt;Data posted to Mercure is always to different topics.&lt;/p&gt;
&lt;p&gt;Posted to topics:
/users/613193cee6be0e1016042334&lt;/p&gt;
&lt;p&gt;Private: true&lt;/p&gt;
&lt;p&gt;Data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    "@context": "/contexts/User",
    "@id": "/users/613193cee6be0e1016042334",
    "@type": "User",
    "id": "613193cee6be0e1016042334",
    "quota": 40000,
    "used": 7186,
    "isDisabled": false,
    "isDeleted": false,
    "createdAt": "2021-09-03T05:17:34+02:00",
    "updatedAt": "2021-09-03T05:18:29+02:00"
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dunglas/mercure/issues/558" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The O(n) growth in topic selector comparisons for each message meant that the computational complexity grows as a product of the number of subscribers and the volume of messages. Highly concurrent access to these caches caused a lot of lock contention which made the Go scheduler a bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The subscriber list is represented as a skipfilter. The test function uses the subscriber's topic selectors (compiled to RegExp) to match the topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For each message, the list of subscribers that will receive the message is retrieved from the skipfilter.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


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



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


&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Each subscriber is stored in a &lt;a href="https://github.com/MauriceGit/skiplist" rel="noopener noreferrer"&gt;skip list&lt;/a&gt;. As subscribers are added, they are assigned an autoincrementing ID and the skip list grows. Skip lists are preferred over slices here due to their discontinuous nature. Subscribers can be added and removed at random and the memory usage will remain bounded.&lt;/p&gt;

&lt;p&gt;Each topic has a &lt;a href="https://github.com/RoaringBitmap/roaring" rel="noopener noreferrer"&gt;roaring bitmap&lt;/a&gt;. Each bit corresponds to a subscriber in the skip list. For each topic, a cursor is maintained to ensure that newly added subscriptions are always tested. Deleted subscriptions are lazily evicted from the topic bitmaps on lookup. Roaring bitmaps are compressed and discontinuous so memory usage again remains bounded as subscribers come and go.&lt;/p&gt;

&lt;p&gt;Finding the complete list of subscribers to which any message must be delivered thus requires only taking the logical &lt;code&gt;And&lt;/code&gt; of a set of bitmaps. This sort of operation has been made exceptionally efficient by leveraging &lt;a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data" rel="noopener noreferrer"&gt;SIMD instructions&lt;/a&gt; on modern CPUs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;A multidimensional benchmark test shows a &lt;em&gt;20,000x improvement&lt;/em&gt; in throughput for certain highly concurrent and highly selective workloads.&lt;/p&gt;


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


&lt;p&gt;An efficiency improvement of &lt;strong&gt;4 orders of magnitude&lt;/strong&gt;.&lt;/p&gt;


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


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

&lt;p&gt;This technique has existed since at least 1980. ZeroMQ refers to it as &lt;a href="http://wiki.zeromq.org/whitepapers:message-matching" rel="noopener noreferrer"&gt;inverted bitmaps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The whitepaper was not discovered until after the implementation was complete. As with most computer algorithms, the creation of skipfilter turns out to be a rediscovery of a technique that predates the birth of its author.&lt;/p&gt;





&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kevburnsjr" rel="noopener noreferrer"&gt;
        kevburnsjr
      &lt;/a&gt; / &lt;a href="https://github.com/kevburnsjr/skipfilter" rel="noopener noreferrer"&gt;
        skipfilter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An inverted bitmap index written in Go.
    &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;Skipfilter&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;This package provides a data structure that combines a skiplist with a roaring bitmap cache.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://godoc.org/github.com/kevburnsjr/skipfilter" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/28e2a80f97f73fe80d2fd67bb0f76517ec2b56a33c1084db47405786b58e784c/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f6b65766275726e736a722f736b697066696c7465723f7374617475732e737667" alt="GoDoc"&gt;&lt;/a&gt;
&lt;a href="https://goreportcard.com/report/github.com/kevburnsjr/skipfilter" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/36170d26bcd0601aee6f0a9276ac9bd2c32b62afb20bff564a4210b20d5f332d/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f6b65766275726e736a722f736b697066696c7465723f33" alt="Go Report Card"&gt;&lt;/a&gt;
&lt;a href="https://raw.githack.com/wiki/kevburnsjr/skipfilter/coverage.html" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkevburnsjr%2Fskipfilter%2Fwiki%2Fcoverage.svg" alt="Go Coverage"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This library was created to efficiently filter a multi-topic message input stream against a set of subscribers,
each having a list of topic subscriptions expressed as regular expressions. Idealy, each subscriber should test
each topic at most once to determine whether it wants to receive messages from the topic.&lt;/p&gt;
&lt;p&gt;In this case, the skip list provides an efficient discontinuous slice of subscribers and the roaring bitmap for each
topic provides an efficient ordered discontinuous set of all subscribers that have indicated that they wish to
receive messages on the topic.&lt;/p&gt;
&lt;p&gt;Filter bitmaps are stored in an LRU cache of variable size (default 100,000).&lt;/p&gt;
&lt;p&gt;This package is theadsafe.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kevburnsjr/skipfilter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>go</category>
      <category>bitmap</category>
      <category>skiplist</category>
    </item>
    <item>
      <title>WebSockets vs Long Polling</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Thu, 22 Jul 2021 20:48:37 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/websockets-vs-long-polling-3a0o</link>
      <guid>https://forem.com/kevburnsjr/websockets-vs-long-polling-3a0o</guid>
      <description>&lt;p&gt;This article contrasts the operational complexity of WebSockets and Long Polling using real world examples to promote Long Polling as a simpler alternative to Websockets in systems where a half-duplex message channel will suffice.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets
&lt;/h3&gt;

&lt;p&gt;A WebSocket is a long lived persistent TCP connection (often utilizing TLS) between a client and a server which provides a real-time full-duplex communication channel. These are often seen in chat applications and real-time dashboards.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long Polling
&lt;/h3&gt;

&lt;p&gt;Long Polling is a near-real-time data access pattern that predates WebSockets. A client initiates a TCP connection (usually an HTTP request) with a maximum duration (ie. 20 seconds). If the server has data to return, it returns the data immediately, usually in batch up to a specified limit. If not, the server pauses the request thread until data becomes available at which point it returns the data to the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;p&gt;WebSockets are Full-Duplex meaning both the client and the server can send and receive messages across the channel. Long Polling is Half-Duplex meaning that a new request-response cycle is required each time the client wants to communicate something to the server. &lt;/p&gt;

&lt;p&gt;Long Polling usually produces slightly higher average latency and significantly higher latency variability compared to WebSockets.&lt;/p&gt;

&lt;p&gt;WebSockets do support compression, but usually per-message. Long Polling typically operates in batch which can significantly improve message compression efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Up
&lt;/h2&gt;

&lt;p&gt;We’ll now contrast the systemic behavior of server-side scalability for applications using primarily WebSockets vs Long Polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets
&lt;/h3&gt;

&lt;p&gt;Suppose we have 4 app servers in a scaling group with 10,000 connected clients.&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%2Fj3zd2q3rkho72ttb7zz4.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%2Fj3zd2q3rkho72ttb7zz4.png" alt="Before" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now suppose we scale up the group by adding a new app server and wait for 60 seconds.&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%2Fxngsdzyq9dbzbn0w43sx.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%2Fxngsdzyq9dbzbn0w43sx.png" alt="After" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We find that all of the existing clients are still connected to the original 4 app servers. The Load Balancer may be intelligent enough to route new connections to the new app server in order to balance the number of concurrent connections so that this effect will diminish over time. However, the amount of time required for this system to return to equilibrium is unknown and theoretically infinite. &lt;/p&gt;

&lt;p&gt;These effects could be mitigated by the application using a system to intelligently preempt web socket connections in response to changes in the scaling group's capacity but this would require the application to have special real-time knowledge about the state of its external environment which crosses a boundary that is typically best left uncrossed without ample justification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long Polling
&lt;/h3&gt;

&lt;p&gt;Suppose we have the same 4 app servers in a scaling group with 10,000 connected clients using Long Polling.&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%2Fj3zd2q3rkho72ttb7zz4.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%2Fj3zd2q3rkho72ttb7zz4.png" alt="Before" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now suppose we scale up the group by adding a new app server and wait for 60 seconds.&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%2Fveripszj5tizajrlyfra.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%2Fveripszj5tizajrlyfra.png" alt="After" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We observe that the number of open connections has automatically rebalanced with no intervention. We can even state declaratively that if the long poll duration is set to 60 seconds or less, then any autoscaling group will automatically regain equilibrium within 60 seconds of any membership change. This trait can be reflected in the application’s Service Level Objectives. These numbers are important because they are used by operators to correctly tune the app’s autoscaling mechanisms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;p&gt;Service Level Objectives are an important aspect of system management since they ultimately serve as the contractual interface between dev and ops. If an application’s ability to return to equilibrium after scaling is unbounded, a change in application behavior is likely warranted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Down
&lt;/h2&gt;

&lt;p&gt;The following example illustrates difficulties encountered by a real world device management software company operating thousands of 24/7 concurrent WebSocket connections from thousands of data collection agents placed inside corporate networks.&lt;/p&gt;

&lt;h3&gt;
  
  
  The System
&lt;/h3&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%2F0v4otyz5to6zy8ybjlcd.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%2F0v4otyz5to6zy8ybjlcd.png" alt="System" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Data Collection Agent, written in Go, is distributed as an executable binary that runs as a service on a customer's machine scanning local networks for SNMP devices and reporting SNMP data periodically to the application in the cloud.&lt;/p&gt;

&lt;p&gt;One key feature of the product was the ability for a customer to interact with any of their devices in real time from anywhere in the world using a single page web application hosted in the cloud. Because each agent resides on a customer network behind a firewall, the agents would need to initiate and maintain a WebSocket connection to the application in the cloud as a secure full-duplex tunnel. The web service sends commands to agents and agents send data to the web service all through a single persistent TCP connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;There was one big unexpected technical challenge faced by the team when deploying this system that made deployments risky. Whenever a new version of the app server was deployed to production, the system would be shocked by high impulse reconnect storms originating from the data collection agents. &lt;/p&gt;

&lt;p&gt;If a server has 2500 active connections and you take it out of service, those 2500 connections will be closed simultaneously and all the agents will reopen new connections simultaneously. This can overwhelm some systems, especially if the socket initialization code touches the database for anything important (ie. authorization). If an agent can’t establish a connection before the read deadline, it will retry the connection again which will drown the app servers even further, causing an unrecoverable negative feedback loop.&lt;/p&gt;

&lt;p&gt;This proclivity toward failure caused management to change their policies regarding deployments to reduce the number of deployments as much as possible to avoid disruption.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;The problem was partially solved by implementing strict exponential retry policies on their clients. This solution was effective enough at reducing the severity of retry storms on app deployment to be considered a good temporary solution. However, deployments were still infrequent by design and the high impulse load spikes weren’t gone, they just no longer produced undesirable secondary effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;p&gt;This temporary solution is only possible in situations where the server has complete control over all of its clients. In many scenarios this may not be the case. &lt;/p&gt;

&lt;p&gt;If the agents were modeled to receive commands from the server by Long Poll and push data to the server through a normal API, the load would be evenly spread. &lt;/p&gt;

&lt;p&gt;If using a Long Poll architecture, the deployment system would replace a node by notifying the load balancer that the node is going out of service to ensure the node doesn’t receive any new connections, then wait 60 seconds for existing connections to drain in accordance with the service’s shutdown grace period SLO, then take the node offline with confidence. The resulting load increase on other nodes in the group would be gradual and roughly linear.&lt;/p&gt;

&lt;p&gt;When it comes to distributed systems and their scalability, people often focus on creating efficient systems. Efficiency is important but usually not as important as stability. High impulse events like reconnect storms can produce complex systemic effects. Left unattended, they often amplify the severity of similar effects in different parts of the system in ways that are both unexpected and difficult to predict.&lt;/p&gt;

&lt;p&gt;If you fail to solve enough of these types of problems, you may soon find yourself a situation where so many components are failing so simultaneously that it’s exceptionally difficult to discern the underlying cause(s) empirically from logs and dashboards. An application’s architecture must be designed primarily in accordance with principle and remain open to modification in response to statistical performance analysis.&lt;/p&gt;

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

&lt;p&gt;WebSockets are appropriate for many applications which require consistent low latency full duplex high frequency communication such as chat applications. However, any WebSocket architecture that can be reduced to a half-duplex problem can probably be remodeled to use Long Polling to improve the application’s runtime performance variability, reducing operational complexity and promoting total systemic stability.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Data Constraints: From Imperative to Declarative</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Thu, 22 Jul 2021 07:23:54 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/data-constraints-from-imperative-to-declarative-2g8d</link>
      <guid>https://forem.com/kevburnsjr/data-constraints-from-imperative-to-declarative-2g8d</guid>
      <description>&lt;p&gt;There once was a client who wanted to port a Lambda from Typescript to Go. Thousands of lines of code spread across dozens of Typescript files, many of them automatically generated database models copy/pasted from other repositories. &lt;/p&gt;

&lt;p&gt;The initiative was sound: To consolidate the business logic spread across various independent lambdas into a single application and in so doing unify a set of often inconsistent data structure definitions.&lt;/p&gt;

&lt;p&gt;You see, the database was MongoDB which meant that the data model was whatever the developer decided the data model was when any given document was inserted into the database by any particular application. Some call this a “schemaless” database but I prefer the term “schema-on-read”. &lt;/p&gt;

&lt;p&gt;Although MongoDB supports json schema validation on write as a functional equivalent to a database schema, this project used none. This is not surprising given that MongoDB’s marketing strategy consists primarily of incessantly trumpeting the advantages of unstructured data to unwitting javascript developers in the most obnoxious and damaging way possible.&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%2F13ki05dkiupbq73cc2g7.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%2F13ki05dkiupbq73cc2g7.jpg" alt="Relational algebra is better than drugs" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I successfully avoided MongoDB for my entire career until one day came a client with an interesting set of problems. Despite my aversion to MongoDB on principle, the strongest advice I could give was to not have used MongoDB in the first place which is sometimes not an option given the unreliable state of present day time travel technology.&lt;/p&gt;

&lt;p&gt;In order to provide meaningful advice to clients on how to use MongoDB well and prevent existing implementations from degrading, I would need an expert knowledge of MongoDB and that could only come from falling neck deep for months into a real world system with truckloads of dynamic unstructured data feeding the needs of real world customers.&lt;/p&gt;

&lt;p&gt;I captured the opportunity and took on many tasks solving complex, elusive problems which often lead to the discovery of undefined behavior. We will focus on one such task: &lt;/p&gt;

&lt;h2&gt;
  
  
  The lambda migration
&lt;/h2&gt;

&lt;p&gt;This lambda wasn’t ready yet. The lambda was known to have bugs and edge cases and the company’s leadership (CEO, COO, CTO) would need to look at it from every conceivable angle to ensure that the business logic was consistent with their expectations before it could be ported to Go. Leadership worked together intensively behind closed doors for a week until they produced a version of the lambda that they felt met the needs of their customers.&lt;/p&gt;

&lt;p&gt;It was my job as a contractor to port the logic from Typescript to Go. I examined the logic. Studied it judiciously. Then I began porting it to Go in such a way that I could run an integration test suite against both my new version in Go and the existing lambda in Typescript to ensure that the output and database effects were identical in all conceivable scenarios.&lt;/p&gt;

&lt;p&gt;Once I was satisfied that the battery of tests proved that my implementation matched the intended logic, a growing suspicion arose that the internally consistent and complete nature of the business logic might indicate that it could be reduced to a set of unique database constraints. &lt;/p&gt;

&lt;p&gt;Raising this concern with the client, I received the following response verbatim which I will treasure with all my heart until the day that I die:&lt;/p&gt;

&lt;h1&gt;
  
  
  “Our business logic is too complex to be expressed as database constraints.”
&lt;/h1&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%2F6ndtl5xgwhugpp13akg9.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%2F6ndtl5xgwhugpp13akg9.png" alt="RIP Isaac Hayes" width="557" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Naturally, I took this as a challenge. After a few hours learning how indexes work in MongoDB, I produced a pair of compound unique indices (one sparse and one full) that would impose the exact same constraints enforced by the business logic on all new and &lt;em&gt;existing&lt;/em&gt; data. &lt;/p&gt;

&lt;p&gt;Extending the new integration test suite to support a third side-by-side implementation quickly proved that the pair of indices did in fact produce identical behavior to the 50 or so lines of business logic (which were now safe to delete) under all conceivable scenarios.&lt;/p&gt;

&lt;p&gt;Leadership was stunned. I had accurately transformed a set of critical business rules from imperative logic to declarative constraints. Management no longer had to reference a spreadsheet with dozens of combinatorial expected outcomes. A description of the expected behavior could be reduced to two statements in plain English.&lt;/p&gt;

&lt;p&gt;However, collections in MongoDB are not the tidy containers of data you expect coming from a decade of experience with schema-on-write databases like MySQL and Postgres.&lt;/p&gt;

&lt;p&gt;When applying the new unique indexes to the collection, the migration failed. The reason it failed is because there existed data in the collection which didn’t match the constraints. This data was inserted into the database at a time when the logic for the lambda(s) was different. The errant data would need to be altered or deleted in order for the constraints to be applied.&lt;/p&gt;

&lt;p&gt;Analysis of this data showed that it drifted from the constraints in a number of different ways. These hundreds of records were reduced to a handful of ways in which they departed from the expected schema. Laid on a spreadsheet was each type of data problem, it’s first occurrence, its last occurrence, the number of occurrences, and a column to track what ought to be done about it (DELETE, FIX, or TBD) along with relevant notes.&lt;/p&gt;

&lt;p&gt;Most of the errant rows could be discarded as complete trash with no discernable value. In some cases the problems could be fixed with a migration. However, there also existed a class of problems where the data was incorrect in a way that could be corrected, except that to do so would produce unexpected behavior for end users. &lt;/p&gt;

&lt;p&gt;There were enough instances of data problems that would adversely affect customers if fixed that it presented a significant challenge to the business. How would they communicate to their B2B customers that they'd been overcharged as a result of a bug in the system? Would they need to issue refunds to those business customers and would those business customers need to issue refunds to &lt;em&gt;their&lt;/em&gt; customers?&lt;/p&gt;

&lt;p&gt;Had the imperative logic not been converted to declarative data constraints, these accounting errors would have continued unnoticed.&lt;/p&gt;

&lt;p&gt;Issues like this are important because left unaddressed they often lead to a culture where it is generally understood (and too often accepted) that customers having longer patronage are more likely to encounter systemic defects than newer customers. This can lead to churn, damaging customer retention and brand reputation.&lt;/p&gt;

&lt;p&gt;This pattern is interesting as it reflects the history of MongoDB itself when users left in droves after the industry became wise to the misleading durability and consistency guarantees used to achieve their early benchmarketing results. MongoDB has come a long way since then and it is now a lot better than it was at not losing data. &lt;/p&gt;

&lt;p&gt;If there is hope for MongoDB, I believe there is hope for humanity.&lt;/p&gt;

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

&lt;p&gt;Modeling business logic as declarative constraints from the outset is likely to save loads of time and significantly improve the quality of your software product. If you can push the line from imperative logic toward declarative constraints you will impress your peers by discovering unknown unknowns.&lt;/p&gt;

</description>
      <category>go</category>
      <category>mongodb</category>
      <category>architecture</category>
      <category>database</category>
    </item>
    <item>
      <title>Five Card Draw for Gophers</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Mon, 24 Jun 2019 05:46:42 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/five-card-draw-for-gophers-l7g</link>
      <guid>https://forem.com/kevburnsjr/five-card-draw-for-gophers-l7g</guid>
      <description>&lt;p&gt;So, I'm on this airplane.&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%2F1iwvlthouufjze0vqx1o.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%2F1iwvlthouufjze0vqx1o.jpg" alt="Airplane" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an 8 hour flight from LAX to Lima with an entertainment system to keep me ... entertained. Before starting a movie, I check out the games section and come across this poker game.&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%2Fh9s3eizhzatac0urk1t1.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%2Fh9s3eizhzatac0urk1t1.jpg" alt="Game" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's five card draw. One player. In Spanish. Buy in is $200 fake dollars and my balance after 30 minutes is $1,585. Ya, this is not a hard game.&lt;/p&gt;

&lt;p&gt;In fact, this game is too easy. Annoyingly easy. Like it's letting me win. Is it possible that this game is actually random and I'm just some genius poker god? Or is the game feeding me cards to let me win? &lt;/p&gt;

&lt;p&gt;This question bothers me. Deeply.&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%2F1f6mwkhlp3ce37e408fc.gif" 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%2F1f6mwkhlp3ce37e408fc.gif" alt="Facepalm" width="500" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hey, wait a minute. I'm a software engineer. I can just clone the game and fiddle with the internals to determine why this implementation seems so unbalanced. &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%2Fxiy92b85fuwc8b2bdky6.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%2Fxiy92b85fuwc8b2bdky6.jpg" alt="Deadpool Reference" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's do it!&lt;/p&gt;




&lt;p&gt;Here's the game: &lt;a href="https://github.com/KevBurnsJr/stupid-poker" rel="noopener noreferrer"&gt;https://github.com/KevBurnsJr/stupid-poker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://stupid-poker.kevburnsjr.com" rel="noopener noreferrer"&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%2Ft1x5byedt703xuq5ro9z.png" width="500" height="320"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;It starts with a deck&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;poker&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;deck&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;52&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;suits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"H"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"T"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"J"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"K"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;newDeck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;suits&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;cards&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s&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="n"&gt;d&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An ordered deck isn't very useful, so let's make it shuffle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// shuffles the deck in place&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shuffle&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a deck, let's create the game&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;stateReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;
    &lt;span class="n"&gt;stateDealt&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Deal initializes the game, returning the player's initial hand and new balance&lt;/span&gt;
    &lt;span class="n"&gt;Deal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Exchange accepts a slice of integers corresponding to indicies for cards in the given hand&lt;/span&gt;
    &lt;span class="c"&gt;// It discards those cards from the hand and replaces them with new cards from the deck&lt;/span&gt;
    &lt;span class="c"&gt;// It returns the final hand and the new balance&lt;/span&gt;
    &lt;span class="n"&gt;Exchange&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&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;The game only has 2 states. Either you are dealing a hand, or you are drawing cards and the hand is over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;deck&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;anty&lt;/span&gt;    &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;hand&lt;/span&gt;    &lt;span class="n"&gt;Hand&lt;/span&gt;
    &lt;span class="n"&gt;mutex&lt;/span&gt;   &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewGame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anty&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;newDeck&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;stateReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;anty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;anty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Deal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrNoBalance&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anty&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newHand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stateDealt&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;stateDealt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;hand&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hand&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;cards&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hand&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;hand&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;hand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;payout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anty&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stateReady&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hand is also an array of strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Hand_RoyalFlush&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Royal Flush"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_StraightFlush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Straight Flush"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Quads&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Four of a Kind"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_FullHouse&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Full House"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Flush&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Flush"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Straight&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Straight"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Trips&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Three of a Kind"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_TwoPair&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Two Pair"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Jacks&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Jacks or Higher"&lt;/span&gt;
    &lt;span class="n"&gt;Hand_Nothing&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Nothing"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Hand&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="c"&gt;// Score scores the hand&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;Hand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Nothing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isFlush&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="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isRoyal&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="n"&gt;Hand_RoyalFlush&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isStraight&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="n"&gt;Hand_StraightFlush&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Flush&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isStraight&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="n"&gt;Hand_Straight&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;highPair&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detectDupes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quads&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Quads&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_FullHouse&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Trips&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_TwoPair&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;highPair&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Jacks&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Hand_Nothing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need some tests to ensure the hand scoring method continues to work as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Table based test to ensure correct scoring of hands&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestScoreHand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"AS KS QS JS TS"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_RoyalFlush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"JD TD AD QD KD"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_RoyalFlush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"KS QS JS TS 9S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_StraightFlush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"KS KH KD KC 9S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Quads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"KS 9S KH KD KC"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Quads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"KS 9S KH KD 9C"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_FullHouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"TS 8S QS JS 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Flush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS KD QS JS TS"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Straight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS AH AD JS TS"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Trips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS AH JS AD 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Trips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS AH JS JD 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_TwoPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS JD 4S AH JS"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_TwoPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS JD AH JS 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_TwoPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"AS AD 8H JS 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Jacks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"JS JD 8H 9S 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Jacks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"TS TD 8H 9S 4S"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hand_Nothing&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="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;newHand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected Result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"!="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fail&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;After that we just slap an interface on it and call it a game :)&lt;br&gt;
&lt;a href="http://stupid-poker.kevburnsjr.com" rel="noopener noreferrer"&gt;http://stupid-poker.kevburnsjr.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the full code on Github&lt;br&gt;
&lt;a href="https://github.com/KevBurnsJr/stupid-poker/tree/master/internal/poker" rel="noopener noreferrer"&gt;https://github.com/KevBurnsJr/stupid-poker/tree/master/internal/poker&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;So... The end result?&lt;/p&gt;

&lt;p&gt;It's still pretty easy to win. That probably means the balance issue has nothing to do with the random number generator and has more to do with the payouts being too high in relation to the the odds. &lt;/p&gt;

&lt;p&gt;We had fun and we built a game. It's not likely to earn us the Turing Award.&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Hi, I'm Kevin Burns</title>
      <dc:creator>Kevin Burns</dc:creator>
      <pubDate>Sun, 23 Jul 2017 18:32:05 +0000</pubDate>
      <link>https://forem.com/kevburnsjr/hi-im-kevin-burns</link>
      <guid>https://forem.com/kevburnsjr/hi-im-kevin-burns</guid>
      <description>&lt;p&gt;I have been coding for 20 years.&lt;/p&gt;

&lt;p&gt;You can find me on GitHub as &lt;a href="https://github.com/KevBurnsJr" rel="noopener noreferrer"&gt;KevBurnsJr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in Menlo Park.&lt;/p&gt;

&lt;p&gt;I work for Dry Roasted Peanuts&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: Go, PHP, Javascript, HTML, CSS.&lt;/p&gt;

&lt;p&gt;I am currently learning more about twice a day.&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
