From b951d8657966f60bf4eb84237a962b80e35ec4dd Mon Sep 17 00:00:00 2001
From: Joe R <joeraz5@verizon.net>
Date: Tue, 11 Jun 2024 20:01:26 -0400
Subject: [PATCH] Added Nines game.

---
 html-src/rules/nines.html | 18 +++++++++++++++
 pysollib/gamedb.py        |  1 +
 pysollib/games/pyramid.py | 47 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 66 insertions(+)
 create mode 100644 html-src/rules/nines.html

diff --git a/html-src/rules/nines.html b/html-src/rules/nines.html
new file mode 100644
index 00000000..2bd76d2e
--- /dev/null
+++ b/html-src/rules/nines.html
@@ -0,0 +1,18 @@
+<h1>Tens</h1>
+<p>
+Pairing game type. 1 deck. No redeal.
+
+<h3>Object</h3>
+<p>
+Move all cards to the single foundation.
+
+<h3>Quick Description</h3>
+<p>
+Like <a href="tens.html">Tens</a>,
+but cards are paired off that total nine, with nines removed
+singly.  Cards from 10 through King are removed in a sequence
+of one of each.
+
+<h3>Notes</h3>
+<p>
+<i>Autodrop</i> is disabled for this game.
diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py
index 7db70cb3..2413f447 100644
--- a/pysollib/gamedb.py
+++ b/pysollib/gamedb.py
@@ -604,6 +604,7 @@ class GI:
          tuple(range(13168, 13170)) + tuple(range(18000, 18005)) +
          tuple(range(19000, 19012)) + tuple(range(22303, 22311)) +
          tuple(range(22353, 22361))),
+        ('dev', tuple(range(961, 962))),
     )
 
     # deprecated - the correct way is to or a GI.GT_XXX flag
diff --git a/pysollib/games/pyramid.py b/pysollib/games/pyramid.py
index dbafbb30..6ef7ff22 100644
--- a/pysollib/games/pyramid.py
+++ b/pysollib/games/pyramid.py
@@ -669,6 +669,7 @@ class SuitElevens(Elevens):
 
 # ************************************************************************
 # * Tens
+# * Nines
 # ************************************************************************
 
 class Tens_RowStack(Elevens_RowStack):
@@ -698,6 +699,50 @@ class Tens(ElevensToo):
         Elevens.createGame(self, rows=2, cols=7, maxpiles=13, reserves=4)
 
 
+class Nines_RowStack(Elevens_RowStack):
+    ACCEPTED_SUM = 7
+
+    def clickHandler(self, event):
+        if not self.cards:
+            return 0
+        c = self.cards[-1]
+        if c.face_up and c.rank == 8 and not self.basicIsBlocked():
+            self.game.playSample("autodrop", priority=20)
+            self.playMoveMove(1, self.game.s.foundations[0], sound=False)
+            return 1
+        return OpenStack.clickHandler(self, event)
+
+
+class Nines_Reserve(Tens_Reserve):
+    def acceptsCards(self, from_stack, cards):
+        if not ReserveStack.acceptsCards(self, from_stack, cards):
+            return False
+        c = cards[0]
+        if c.rank not in self.ACCEPTED_CARDS:
+            return False
+        for s in self.game.s.reserves:
+            if s.cards and s.cards[0].rank == c.rank:
+                return False
+        return True
+
+
+class Nines_Foundation(AbstractFoundationStack):
+    def acceptsCards(self, from_stack, cards):
+        if cards[0].rank == 8:
+            return True
+        # We accept any nine. Pairs will get delivered by _dropPairMove.
+        return AbstractFoundationStack.acceptsCards(self, from_stack, cards)
+
+
+class Nines(Tens):
+    RowStack_Class = Nines_RowStack
+    Reserve_Class = Nines_Reserve
+    Foundation_Class = Nines_Foundation
+
+    def createGame(self):
+        Elevens.createGame(self, rows=3, cols=3, reserves=4)
+
+
 # ************************************************************************
 # * The Lucky Number
 # ************************************************************************
@@ -1829,3 +1874,5 @@ registerGame(GameInfo(937, TheLuckyNumber, "The Lucky Number",
 registerGame(GameInfo(950, Eighteens, "Eighteens",
                       GI.GT_PAIRING_TYPE, 2, 0, GI.SL_MOSTLY_LUCK,
                       altnames=("Steel Wheels",)))
+registerGame(GameInfo(961, Nines, "Nines",
+                      GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))