1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00

Compare commits

...

9 commits

Author SHA1 Message Date
Shlomi Fish
6bc5b53995 test presence of a <move/> element
add a testcase for the XML output
2025-02-01 13:52:43 +02:00
Shlomi Fish
dd42ac10ea test presence of a <move/> element
add a testcase for the XML output
2025-01-31 09:05:30 +02:00
Shlomi Fish
d6ca6d226b add a testcase for the XML output 2025-01-31 08:39:36 +02:00
Shlomi Fish
e4e2e99031 [Wip] kpatience xml exporting (Jan 2025 try) 2025-01-31 08:39:36 +02:00
Joe R
054c0f0368 Enable log for demo games 2025-01-28 23:32:15 -05:00
Joe R
487d1c52f3 Use Python 3.13 in AppVeyor 2025-01-28 19:38:23 -05:00
Joe R
6852bb40ff Add Little Ishido game 2025-01-24 21:58:56 -05:00
Joe R
6006e0f4c0 Shift children's games to use tag rather than deprecated list 2025-01-24 20:03:08 -05:00
Joe R
68138c7284 Add Colours game 2025-01-23 18:01:20 -05:00
27 changed files with 306 additions and 53 deletions

View file

@ -2,7 +2,7 @@
image: Visual Studio 2022 image: Visual Studio 2022
environment: environment:
matrix: matrix:
- PYTHON: "C:\\Python312" - PYTHON: "C:\\Python313"
# Shamelessly taken from https://github.com/plicease/Dist-Zilla-PluginBundle-Author-Plicease/blob/master/.appveyor.yml # Shamelessly taken from https://github.com/plicease/Dist-Zilla-PluginBundle-Author-Plicease/blob/master/.appveyor.yml
# Thanks! # Thanks!
install: install:

View file

@ -0,0 +1,15 @@
<h1>Colours</h1>
<p>
Numerica type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="ladybetty.html">Lady Betty</a>,
but the foundations are built up by same color, and
wrap from king to ace. At the start of the game, a
two through five are dealt to the foundations, in
alternating colors.

View file

@ -0,0 +1,12 @@
<h1>Little Ishido</h1>
<p>
Ishido game type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all tiles to the playing area.
<h3>Quick Description</h3>
<p>
Like <a href='ishido.html'>Ishido</a>, but with four colors and symbols,
and a 6X8 grid. The initial tiles are placed in the four corners only.

View file

@ -0,0 +1,14 @@
<h1>Little Ishido Relaxed</h1>
<p>
Ishido game type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all tiles to the playing area.
<h3>Quick Description</h3>
<p>
Like <a href='ishido.html'>Ishido</a>, but with four colors and symbols,
and a 6X8 grid. The initial tiles are placed in the four corners only.
Also, there are no restrictions when placing a tile next to two or more
other tiles - they just have to match the color or symbol of each.

View file

@ -4370,6 +4370,9 @@ msgstr "Aktuelle Sitzung"
msgid "Log" msgid "Log"
msgstr "Protokoll" msgstr "Protokoll"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Volles Protokoll" msgstr "Volles Protokoll"
@ -5171,6 +5174,9 @@ msgstr "Kommentare..."
msgid "Log..." msgid "Log..."
msgstr "" msgstr ""
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "D&emo Statistiken..." msgstr "D&emo Statistiken..."

View file

@ -4421,6 +4421,9 @@ msgstr "Session en cours"
msgid "Log" msgid "Log"
msgstr "Journal" msgstr "Journal"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Journal complet" msgstr "Journal complet"
@ -5222,6 +5225,9 @@ msgstr "&Commentaires..."
msgid "Log..." msgid "Log..."
msgstr "Journal..." msgstr "Journal..."
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "Statistiques d&émo..." msgstr "Statistiques d&émo..."

View file

@ -4485,6 +4485,9 @@ msgstr "Questa sessione"
msgid "Log" msgid "Log"
msgstr "Log" msgstr "Log"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Log completo" msgstr "Log completo"
@ -5288,6 +5291,9 @@ msgstr "&Commenti..."
msgid "Log..." msgid "Log..."
msgstr "Log..." msgstr "Log..."
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "Statistiche d&emo..." msgstr "Statistiche d&emo..."

View file

@ -4438,6 +4438,9 @@ msgstr "Bieżąca sesja"
msgid "Log" msgid "Log"
msgstr "Log" msgstr "Log"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Kompletny log" msgstr "Kompletny log"
@ -5242,6 +5245,9 @@ msgstr "Komentarze..."
msgid "Log..." msgid "Log..."
msgstr "Log..." msgstr "Log..."
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "D&emo statystyk..." msgstr "D&emo statystyk..."

View file

@ -4444,6 +4444,9 @@ msgstr "Sessão atual"
msgid "Log" msgid "Log"
msgstr "registro" msgstr "registro"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Registro completo" msgstr "Registro completo"
@ -5244,6 +5247,9 @@ msgstr "&Comentários..."
msgid "Log..." msgid "Log..."
msgstr "Registro" msgstr "Registro"
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "Estatísticas de D&emo..." msgstr "Estatísticas de D&emo..."

View file

@ -4224,6 +4224,9 @@ msgstr ""
msgid "Log" msgid "Log"
msgstr "" msgstr ""
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "" msgstr ""
@ -4974,6 +4977,9 @@ msgstr ""
msgid "Log..." msgid "Log..."
msgstr "" msgstr ""
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "" msgstr ""

View file

@ -4494,6 +4494,9 @@ msgstr "Текущая сессия"
msgid "Log" msgid "Log"
msgstr "Лог" msgstr "Лог"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404 #: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log" msgid "Full log"
msgstr "Полный лог" msgstr "Полный лог"
@ -5311,6 +5314,9 @@ msgstr "&Комментарии..."
msgid "Log..." msgid "Log..."
msgstr "Лог..." msgstr "Лог..."
msgid "Demo Log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427 #: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..." msgid "D&emo statistics..."
msgstr "Статистика демо..." msgstr "Статистика демо..."

View file

@ -115,18 +115,13 @@ class Statistics:
game.GAME_VERSION) game.GAME_VERSION)
# full log # full log
if status >= 0: if status >= 0:
if player is None: if player not in self.prev_games:
# demo self.prev_games[player] = []
ret = self.updateGameStat(player, game, status) self.prev_games[player].append(log)
else: if player not in self.all_prev_games:
# player self.all_prev_games[player] = []
if player not in self.prev_games: self.all_prev_games[player].append(log)
self.prev_games[player] = [] ret = self.updateGameStat(player, game, status)
self.prev_games[player].append(log)
if player not in self.all_prev_games:
self.all_prev_games[player] = []
self.all_prev_games[player].append(log)
ret = self.updateGameStat(player, game, status)
# session log # session log
if player not in self.session_games: if player not in self.session_games:
self.session_games[player] = [] self.session_games[player] = []

View file

@ -595,13 +595,12 @@ class GI:
tuple(range(19000, 19012)) + tuple(range(22303, 22311)) + tuple(range(19000, 19012)) + tuple(range(22303, 22311)) +
tuple(range(22353, 22361))), tuple(range(22353, 22361))),
('fc-3.1', tuple(range(961, 971))), ('fc-3.1', tuple(range(961, 971))),
('dev', tuple(range(971, 972))), ('dev', tuple(range(971, 973)) + tuple(range(18005, 18007))),
) )
# deprecated - the correct way is to or a GI.GT_XXX flag # deprecated - the correct way is to or a GI.GT_XXX flag
# in the registerGame() call # in the registerGame() call
_CHILDREN_GAMES = [16, 33, 55, 90, 91, 96, 97, 176, 328, 329, 862, 865, _CHILDREN_GAMES = []
886, 903, ]
_OPEN_GAMES = [] _OPEN_GAMES = []

View file

@ -504,7 +504,7 @@ class MaineCoon(TabbyCat):
# register the game # register the game
registerGame(GameInfo(903, AcesUp, "Aces Up", # was: 52 registerGame(GameInfo(903, AcesUp, "Aces Up", # was: 52
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK, GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK,
altnames=("Aces High", "Drivel", "Discard"))) altnames=("Aces High", "Drivel", "Discard")))
registerGame(GameInfo(206, Fortunes, "Fortunes", registerGame(GameInfo(206, Fortunes, "Fortunes",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK)) GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK))
@ -514,7 +514,7 @@ registerGame(GameInfo(130, PerpetualMotion, "Perpetual Motion",
GI.GT_1DECK_TYPE, 1, -1, GI.SL_MOSTLY_LUCK, GI.GT_1DECK_TYPE, 1, -1, GI.SL_MOSTLY_LUCK,
altnames=("First Law", "Narcotic"))) altnames=("First Law", "Narcotic")))
registerGame(GameInfo(353, AcesUp5, "Aces Up 5", registerGame(GameInfo(353, AcesUp5, "Aces Up 5",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK)) GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(552, Cover, "Cover", registerGame(GameInfo(552, Cover, "Cover",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK)) GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(583, FiringSquad, "Firing Squad", registerGame(GameInfo(583, FiringSquad, "Firing Squad",

View file

@ -105,6 +105,7 @@ class StrictEiffelTower(EiffelTower):
# register the game # register the game
registerGame(GameInfo(16, EiffelTower, "Eiffel Tower", registerGame(GameInfo(16, EiffelTower, "Eiffel Tower",
GI.GT_PAIRING_TYPE, 2, 0, GI.SL_MOSTLY_LUCK)) GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 2, 0,
GI.SL_MOSTLY_LUCK))
# registerGame(GameInfo(801, StrictEiffelTower, "Strict Eiffel Tower", # registerGame(GameInfo(801, StrictEiffelTower, "Strict Eiffel Tower",
# GI.GT_PAIRING_TYPE, 2, 0)) # GI.GT_PAIRING_TYPE, 2, 0))

View file

@ -776,11 +776,11 @@ registerGame(GameInfo(697, BigBen, "Big Ben",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED, GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED,
altnames=("Father Time"))) altnames=("Father Time")))
registerGame(GameInfo(737, Clock, "Clock", registerGame(GameInfo(737, Clock, "Clock",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK, GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK,
altnames=("Travellers", "Sundial"))) altnames=("Travellers", "Sundial")))
registerGame(GameInfo(827, GermanClock, "German Clock", registerGame(GameInfo(827, GermanClock, "German Clock",
GI.GT_1DECK_TYPE, 1, 1, GI.SL_MOSTLY_LUCK, GI.GT_1DECK_TYPE, 1, 1, GI.SL_MOSTLY_LUCK,
altnames=("Die Uhr",))) altnames=("Die Uhr",)))
registerGame(GameInfo(915, RelaxedClock, "Relaxed Clock", registerGame(GameInfo(915, RelaxedClock, "Relaxed Clock",
GI.GT_1DECK_TYPE | GI.GT_RELAXED, 1, 0, GI.SL_LUCK, GI.GT_1DECK_TYPE | GI.GT_RELAXED | GI.GT_CHILDREN, 1, 0,
altnames=("Watch"))) GI.SL_LUCK, altnames=("Watch")))

View file

@ -170,5 +170,5 @@ registerGame(GameInfo(774, HitOrMiss, "Hit or Miss",
GI.GT_1DECK_TYPE, 1, VARIABLE_REDEALS, GI.GT_1DECK_TYPE, 1, VARIABLE_REDEALS,
GI.SL_LUCK, altnames=("Roll Call",))) GI.SL_LUCK, altnames=("Roll Call",)))
registerGame(GameInfo(865, HitOrMissUnlimited, "Hit or Miss Unlimited", registerGame(GameInfo(865, HitOrMissUnlimited, "Hit or Miss Unlimited",
GI.GT_1DECK_TYPE, 1, UNLIMITED_REDEALS, GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, UNLIMITED_REDEALS,
GI.SL_LUCK)) GI.SL_LUCK))

View file

@ -1640,7 +1640,8 @@ registerGame(GameInfo(66, Eastcliff, "Eastcliff",
registerGame(GameInfo(224, Easthaven, "Easthaven", registerGame(GameInfo(224, Easthaven, "Easthaven",
GI.GT_GYPSY, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_GYPSY, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(33, Westcliff, "Westcliff", registerGame(GameInfo(33, Westcliff, "Westcliff",
GI.GT_KLONDIKE, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_KLONDIKE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(225, Westhaven, "Westhaven", registerGame(GameInfo(225, Westhaven, "Westhaven",
GI.GT_GYPSY, 1, 0, GI.SL_BALANCED)) GI.GT_GYPSY, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(107, PasSeul, "Pas Seul", registerGame(GameInfo(107, PasSeul, "Pas Seul",

View file

@ -1118,15 +1118,16 @@ registerGame(GameInfo(216, MonteCarlo2Decks, "Monte Carlo (2 Decks)",
registerGame(GameInfo(212, Weddings, "Weddings", registerGame(GameInfo(212, Weddings, "Weddings",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(90, SimpleCarlo, "Simple Carlo", registerGame(GameInfo(90, SimpleCarlo, "Simple Carlo",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(91, SimplePairs, "Simple Pairs", registerGame(GameInfo(91, SimplePairs, "Simple Pairs",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK, GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 1, 0,
altnames=("Jamestown", "Pirate Gold", "Treasure Hunt", GI.SL_LUCK, altnames=("Jamestown", "Pirate Gold",
"Hunter"))) "Treasure Hunt", "Hunter")))
registerGame(GameInfo(92, Neighbour, "Neighbour", registerGame(GameInfo(92, Neighbour, "Neighbour",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(96, Fourteen, "Fourteen", registerGame(GameInfo(96, Fourteen, "Fourteen",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.GT_PAIRING_TYPE | GI.GT_OPEN | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK, altnames=("Fourteen Out", GI.SL_MOSTLY_LUCK, altnames=("Fourteen Out",
"Fourteen Puzzle", "Fourteen Puzzle",
"Take Fourteen"))) "Take Fourteen")))
@ -1137,12 +1138,13 @@ registerGame(GameInfo(152, DerLetzteMonarch, "The Last Monarch",
GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL, GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL,
altnames=("Der letzte Monarch",))) altnames=("Der letzte Monarch",)))
registerGame(GameInfo(328, TheWish, "The Wish", registerGame(GameInfo(328, TheWish, "The Wish",
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED, 1, 0, GI.GT_PAIRING_TYPE | GI.GT_STRIPPED | GI.GT_CHILDREN,
GI.SL_MOSTLY_LUCK, ranks=(0, 6, 7, 8, 9, 10, 11, 12))) 1, 0, GI.SL_MOSTLY_LUCK,
ranks=(0, 6, 7, 8, 9, 10, 11, 12)))
registerGame(GameInfo(329, TheWishOpen, "The Wish (Open)", registerGame(GameInfo(329, TheWishOpen, "The Wish (Open)",
GI.GT_PAIRING_TYPE | GI.GT_OPEN | GI.GT_ORIGINAL | GI.GT_PAIRING_TYPE | GI.GT_OPEN | GI.GT_ORIGINAL |
GI.GT_STRIPPED, 1, 0, GI.SL_MOSTLY_SKILL, GI.GT_STRIPPED | GI.GT_CHILDREN, 1, 0,
ranks=(0, 6, 7, 8, 9, 10, 11, 12), GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12),
rules_filename="thewish.html")) rules_filename="thewish.html"))
registerGame(GameInfo(368, Vertical, "Vertical", registerGame(GameInfo(368, Vertical, "Vertical",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0,
@ -1162,8 +1164,8 @@ registerGame(GameInfo(810, Quatorze, "Quatorze",
registerGame(GameInfo(829, BlockTen, "Block Ten", registerGame(GameInfo(829, BlockTen, "Block Ten",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK)) GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(862, SimpleTens, "Simple Tens", registerGame(GameInfo(862, SimpleTens, "Simple Tens",
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED, 1, 0, GI.SL_LUCK, GI.GT_PAIRING_TYPE | GI.GT_STRIPPED | GI.GT_CHILDREN,
ranks=(0, 1, 2, 3, 4, 5, 6, 7, 8), 1, 0, GI.SL_LUCK, ranks=(0, 1, 2, 3, 4, 5, 6, 7, 8),
altnames=("Add Up Tens",))) altnames=("Add Up Tens",)))
registerGame(GameInfo(867, DoubleFourteen, "Double Fourteen", registerGame(GameInfo(867, DoubleFourteen, "Double Fourteen",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 2, 0, GI.GT_PAIRING_TYPE | GI.GT_OPEN, 2, 0,

View file

@ -41,6 +41,7 @@ from pysollib.stack import \
RK_FoundationStack, \ RK_FoundationStack, \
RK_RowStack, \ RK_RowStack, \
ReserveStack, \ ReserveStack, \
SC_FoundationStack, \
SS_FoundationStack, \ SS_FoundationStack, \
Stack, \ Stack, \
StackWrapper, \ StackWrapper, \
@ -176,6 +177,7 @@ class Numerica2Decks(Numerica):
# ************************************************************************ # ************************************************************************
# * Lady Betty # * Lady Betty
# * Last Chance # * Last Chance
# * Colours
# ************************************************************************ # ************************************************************************
class LadyBetty(Numerica): class LadyBetty(Numerica):
@ -215,6 +217,40 @@ class LastChance(LadyBetty):
self.s.talon.dealCards() self.s.talon.dealCards()
class Colours(LadyBetty):
Foundation_Class = StackWrapper(SC_FoundationStack, mod=13, suit=ANY_SUIT)
def startGame(self):
self.startDealSample()
self.s.talon.dealRow(rows=self.s.foundations)
self.s.talon.dealCards() # deal first card to WasteStack
def _shuffleHook(self, cards):
# prepare first cards
topcards = [None] * 4
evencolor = -1
oddcolor = -1
for c in cards[:]:
if 0 < c.rank <= 4 and topcards[c.rank - 1] is None:
if c.rank % 2 == 0:
if evencolor != -1 and c.color != evencolor:
continue
if oddcolor != -1 and c.color == oddcolor:
continue
evencolor = c.color
elif c.rank % 2 == 1:
if oddcolor != -1 and c.color != oddcolor:
continue
if evencolor != -1 and c.color == evencolor:
continue
oddcolor = c.color
topcards[c.rank - 1] = c
cards.remove(c)
topcards.reverse()
return cards + topcards
# ************************************************************************ # ************************************************************************
# * Puss in the Corner # * Puss in the Corner
# ************************************************************************ # ************************************************************************
@ -1460,3 +1496,5 @@ registerGame(GameInfo(931, TheBogey, "The Bogey",
GI.GT_1DECK_TYPE, 1, -1, GI.SL_BALANCED)) GI.GT_1DECK_TYPE, 1, -1, GI.SL_BALANCED))
registerGame(GameInfo(958, NinetyOne, "Ninety-One", registerGame(GameInfo(958, NinetyOne, "Ninety-One",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_SKILL)) GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(972, Colours, "Colours",
GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED))

View file

@ -1573,13 +1573,14 @@ registerGame(GameInfo(54, RoyalCotillion, "Royal Cotillion",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_LUCK, GI.GT_2DECK_TYPE, 2, 0, GI.SL_LUCK,
altnames=("Lords and Ladies",))) altnames=("Lords and Ladies",)))
registerGame(GameInfo(55, OddAndEven, "Odd and Even", registerGame(GameInfo(55, OddAndEven, "Odd and Even",
GI.GT_2DECK_TYPE, 2, 1, GI.SL_LUCK)) GI.GT_2DECK_TYPE | GI.GT_CHILDREN, 2, 1, GI.SL_LUCK))
registerGame(GameInfo(143, Kingdom, "Kingdom", registerGame(GameInfo(143, Kingdom, "Kingdom",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_LUCK)) GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(234, Alhambra, "Alhambra", registerGame(GameInfo(234, Alhambra, "Alhambra",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED))
registerGame(GameInfo(97, Carpet, "Carpet", registerGame(GameInfo(97, Carpet, "Carpet",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(391, BritishConstitution, "British Constitution", registerGame(GameInfo(391, BritishConstitution, "British Constitution",
GI.GT_2DECK_TYPE | GI.GT_STRIPPED, 2, 0, GI.SL_BALANCED, GI.GT_2DECK_TYPE | GI.GT_STRIPPED, 2, 0, GI.SL_BALANCED,
ranks=list(range(11)), # without Queens and Kings ranks=list(range(11)), # without Queens and Kings

View file

@ -99,6 +99,9 @@ class Ishido(Game):
STRICT_FOUR_WAYS = True STRICT_FOUR_WAYS = True
SCORING = False SCORING = False
COLS = 12
ROWS = 8
# #
# game layout # game layout
# #
@ -115,13 +118,13 @@ class Ishido(Game):
w2 = max(2 * l.XS, x) w2 = max(2 * l.XS, x)
# set window # set window
w, h = w2 + l.XM * 2 + l.CW * 12, l.YM * 2 + l.CH * 8 w, h = w2 + l.XM * 2 + l.CW * self.COLS, l.YM * 2 + l.CH * self.ROWS
self.setSize(w, h) self.setSize(w, h)
# Create rows # Create rows
for j in range(8): for j in range(self.ROWS):
x, y = w2 + l.XM, l.YM + l.CH * j x, y = w2 + l.XM, l.YM + l.CH * j
for i in range(12): for i in range(self.COLS):
s.rows.append(self.RowStack_Class(x, y, self)) s.rows.append(self.RowStack_Class(x, y, self))
x = x + l.CW x = x + l.CW
@ -269,17 +272,17 @@ class Ishido(Game):
def getAdjacent(self, playSpace): def getAdjacent(self, playSpace):
adjacentRows = [] adjacentRows = []
if playSpace % 12 != 11: if playSpace % self.COLS != self.COLS - 1:
adjacentRows.append(self.s.rows[playSpace + 1]) adjacentRows.append(self.s.rows[playSpace + 1])
if playSpace % 12 != 0: if playSpace % self.COLS != 0:
adjacentRows.append(self.s.rows[playSpace - 1]) adjacentRows.append(self.s.rows[playSpace - 1])
if playSpace + 12 < 96: if playSpace + self.COLS < (self.COLS * self.ROWS):
adjacentRows.append(self.s.rows[playSpace + 12]) adjacentRows.append(self.s.rows[playSpace + self.COLS])
if playSpace - 12 > -1: if playSpace - self.COLS > -1:
adjacentRows.append(self.s.rows[playSpace - 12]) adjacentRows.append(self.s.rows[playSpace - self.COLS])
return adjacentRows return adjacentRows
@ -301,10 +304,46 @@ class IshidoScored(Ishido):
SCORING = True SCORING = True
class LittleIshido(Ishido):
ROWS = 6
COLS = 8
def startGame(self):
self.score = 0
self.fourways = 0
self.moveMove(1, self.s.talon, self.s.rows[0], frames=0)
self.s.rows[0].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[7], frames=0)
self.s.rows[7].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[40], frames=0)
self.s.rows[40].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[47], frames=0)
self.s.rows[47].flipMove()
self.s.talon.fillStack()
def _shuffleHook(self, cards):
# prepare first cards
symbols = []
colors = []
topcards = []
for c in cards[:]:
if c.suit not in colors and c.rank not in symbols:
topcards.append(c)
cards.remove(c)
symbols.append(c.rank)
colors.append(c.suit)
if len(colors) >= 4 or len(symbols) >= 4:
break
return cards + topcards
class LittleIshidoRelaxed(LittleIshido):
STRICT_FOUR_WAYS = False
def r(id, gameclass, name, decks, redeals, skill_level, def r(id, gameclass, name, decks, redeals, skill_level,
game_type=GI.GT_ISHIDO): game_type=GI.GT_ISHIDO, colors=6):
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level, gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
ranks=list(range(6)), suits=list(range(6)), ranks=list(range(colors)), suits=list(range(colors)),
category=GI.GC_ISHIDO) category=GI.GC_ISHIDO)
registerGame(gi) registerGame(gi)
return gi return gi
@ -316,3 +355,6 @@ r(18002, FreeIshido, 'Free Ishido', 2, 0, GI.SL_MOSTLY_SKILL)
r(18003, FreeIshidoRelaxed, 'Free Ishido Relaxed', 2, 0, GI.SL_MOSTLY_SKILL) r(18003, FreeIshidoRelaxed, 'Free Ishido Relaxed', 2, 0, GI.SL_MOSTLY_SKILL)
r(18004, IshidoScored, 'Ishido Scored', 2, 0, GI.SL_MOSTLY_SKILL, r(18004, IshidoScored, 'Ishido Scored', 2, 0, GI.SL_MOSTLY_SKILL,
game_type=GI.GT_ISHIDO | GI.GT_SCORE) game_type=GI.GT_ISHIDO | GI.GT_SCORE)
r(18005, LittleIshido, 'Little Ishido', 2, 0, GI.SL_MOSTLY_SKILL, colors=4)
r(18006, LittleIshidoRelaxed, 'Little Ishido Relaxed', 2, 0,
GI.SL_MOSTLY_SKILL, colors=4)

View file

@ -392,12 +392,12 @@ class MemorySequence(Memory24):
# register the game # register the game
registerGame(GameInfo(886, Memory16, "Memory 16", registerGame(GameInfo(886, Memory16, "Memory 16",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL, GI.GT_MEMORY | GI.GT_SCORE | GI.GT_CHILDREN, 2, 0,
category=GI.GC_MATCHING, GI.SL_SKILL, category=GI.GC_MATCHING,
suits=(), ranks=(), trumps=list(range(8)))) suits=(), ranks=(), trumps=list(range(8))))
registerGame(GameInfo(176, Memory24, "Memory 24", registerGame(GameInfo(176, Memory24, "Memory 24",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL, GI.GT_MEMORY | GI.GT_SCORE | GI.GT_CHILDREN, 2, 0,
category=GI.GC_MATCHING, GI.SL_SKILL, category=GI.GC_MATCHING,
suits=(), ranks=(), trumps=list(range(12)))) suits=(), ranks=(), trumps=list(range(12))))
registerGame(GameInfo(219, Memory30, "Memory 30", registerGame(GameInfo(219, Memory30, "Memory 30",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL, GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL,

View file

@ -26,6 +26,8 @@ import os
import re import re
import subprocess import subprocess
import time import time
from collections import OrderedDict
from io import BytesIO from io import BytesIO
from pysollib.mfxutil import destruct from pysollib.mfxutil import destruct
@ -996,6 +998,72 @@ class FreeCellSolver_Hint(Base_Solver_Hint):
-1 -1
) )
def calcBoardXML(self):
from io import StringIO
from xml.sax.saxutils import XMLGenerator
game = self.game
self.board = ''
is_simple_simon = self._isSimpleSimon()
b = []
for s in game.s.foundations:
if s.cards:
b.append(s.cards[0 if is_simple_simon else -1])
assert len(b) == 0
b = []
for s in game.s.reserves:
b.append((s.cards[-1]) if s.cards else None)
assert all(x is None for x in b)
nextid = 1
ids = {}
out = StringIO("")
xmler = XMLGenerator(out=out, encoding='UTF-8')
xmler.startDocument()
xmler.startElement(name='state', attrs={})
for row_idx, s in enumerate(game.s.rows):
moveattrs = []
moveattrs.append(('pile', 'store{}'.format(row_idx)))
moveattrs.append(('position', '{}'.format(0)))
moveattrs.sort()
dmoveattrs = OrderedDict(moveattrs)
xmler.startElement(name='move', attrs=dmoveattrs)
b = []
for c in s.cards:
cardattrs = []
cs = self.card2str1(c)
if cs in ids:
this_id = ids[cs]
else:
this_id = nextid
nextid += 1
ids[cs] = this_id
cardattrs.append(('id', '{}'.format(this_id)))
this_rank = [
"ace", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "ten", "jack", "queen", "king"
][c.rank]
cardattrs.append(('rank', '{}'.format(this_rank)))
this_suit = [
"clubs", "spades", "hearts", "diamonds",
][c.suit]
cardattrs.append(('suit', '{}'.format(this_suit)))
cardattrs.append(('turn', '{}'.format("face-up")))
cardattrs.sort()
dcardattrs = OrderedDict(cardattrs)
xmler.startElement(name='card', attrs=dcardattrs)
if not c.face_up:
cs = '<%s>' % cs
xmler.endElement(name='card')
xmler.endElement(name='move')
xmler.endElement(name='state')
xmler.endDocument()
ret = out.getvalue()
return ret
def calcBoardString(self): def calcBoardString(self):
game = self.game game = self.game
self.board = '' self.board = ''

View file

@ -377,7 +377,7 @@ class TreeFormatter(PysolStatsFormatter):
return 1 return 1
def writeLog(self, player, prev_games, sort_by='date'): def writeLog(self, player, prev_games, sort_by='date'):
if not player or not prev_games: if not prev_games:
return 0 return 0
num_rows = 0 num_rows = 0
results = self.getLogResults(player, prev_games) results = self.getLogResults(player, prev_games)
@ -520,6 +520,8 @@ class LogDialog(MfxDialog):
kw = self.initKw(kw) kw = self.initKw(kw)
title = _('Log') title = _('Log')
if player is None:
title = _('Demo Log')
MfxDialog.__init__(self, parent, title, kw.resizable, kw.default) MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
self.top.wm_minsize(400, 200) self.top.wm_minsize(400, 200)

View file

@ -620,6 +620,9 @@ class PysolMenubarTkCommon:
menu.add_command( menu.add_command(
label=n_("Log..."), label=n_("Log..."),
command=lambda: self.mPlayerStats(mode=103)) command=lambda: self.mPlayerStats(mode=103))
menu.add_command(
label=n_("Demo Log..."),
command=lambda: self.mPlayerStats(mode=1103))
menu.add_separator() menu.add_separator()
menu.add_command( menu.add_command(
label=n_("&Comments..."), label=n_("&Comments..."),

View file

@ -50,6 +50,24 @@ class ImportFileTests(unittest.TestCase):
def _successful_import(self, fn, want_s, blurb): def _successful_import(self, fn, want_s, blurb):
self.assertEqual(self._calc_hint(fn).calcBoardString(), want_s, blurb) self.assertEqual(self._calc_hint(fn).calcBoardString(), want_s, blurb)
def _successful_import__XML_test(self, fn, expected_regex, blurb):
hint = self._calc_hint(fn)
xml_output = hint.calcBoardXML()
# import sys
# print(xml_output, file=sys.stderr)
self.assertRegex(
text=xml_output, expected_regex=expected_regex, msg=blurb)
def test_import_XML(self):
return self._successful_import__XML_test(
fn='tests/unit/data/with-10-for-rank.txt',
expected_regex=(
'''<state><move pile="store0" position="0">'''
'''<card id="[0-9]+" rank="four"'''
''' suit="clubs" turn="face-up"></card>'''
),
blurb='xml import worked')
def test_import(self): def test_import(self):
return self._successful_import('tests/unit/data/with-10-for-rank.txt', return self._successful_import('tests/unit/data/with-10-for-rank.txt',
'''FC: - - - - '''FC: - - - -