diff --git a/html-src/gen-html.py b/html-src/gen-html.py index a4ea96ef..82a739f2 100755 --- a/html-src/gen-html.py +++ b/html-src/gen-html.py @@ -71,6 +71,7 @@ rules_files = [ ('shisensho.html', 'PySol - Rules for Shisen-Sho'), ('spider.html', 'PySol - Rules for Spider'), ('freecell.html', 'PySol - Rules for FreeCell'), + ('lightsout.html', 'PySol - Rules for Lights Out'), ] wikipedia_files = [ ('houseinthewood.html', 'PySol - Rules for House in the Woods'), diff --git a/html-src/rules/lightsout.html b/html-src/rules/lightsout.html new file mode 100644 index 00000000..cc56caf4 --- /dev/null +++ b/html-src/rules/lightsout.html @@ -0,0 +1,15 @@ +

Lights Out

+Lights Out type. One deck. No redeals. + +

Object

+Flip all cards face-down. + +

Rules

+

+A grid of cards are dealt in a grid, with some face-up and +others face-down. Any single card can be flipped - if a card +is flipped, all of its adjacent cards horizontally or vertically +are flipped as well. +

+The game is won if every card is flipped face-down at the same +time. diff --git a/po/de_pysol.po b/po/de_pysol.po index 9b9d60c2..8daa7c4d 100644 --- a/po/de_pysol.po +++ b/po/de_pysol.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: PySol 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-10 10:19-0500\n" -"PO-Revision-Date: 2021-12-28 13:52-0500\n" +"PO-Revision-Date: 2022-01-01 23:20-0500\n" "Last-Translator: H. Schaekel \n" "Language-Team: German\n" "Language: de\n" @@ -453,6 +453,9 @@ msgstr "4 Deck Spiele" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "Matrix" @@ -600,6 +603,9 @@ msgstr "Puzzle" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/po/fr_pysol.po b/po/fr_pysol.po index 59280410..cefaacba 100644 --- a/po/fr_pysol.po +++ b/po/fr_pysol.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: 1.02\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-10 10:19-0500\n" -"PO-Revision-Date: 2021-12-28 13:52-0500\n" +"PO-Revision-Date: 2022-01-01 23:20-0500\n" "Last-Translator: Eric Rausch \n" "Language-Team: French\n" "Language: fr\n" @@ -459,6 +459,9 @@ msgstr "Jeux à 4 jeux de cartes" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "Matrice" @@ -606,6 +609,9 @@ msgstr "Type Puzzle" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/po/it_pysol.po b/po/it_pysol.po index c236d2d2..c8853306 100644 --- a/po/it_pysol.po +++ b/po/it_pysol.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: it_pysol\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-10 10:19-0500\n" -"PO-Revision-Date: 2021-12-28 13:52-0500\n" +"PO-Revision-Date: 2022-01-01 23:21-0500\n" "Last-Translator: Giuliano Colla \n" "Language-Team: Italiano \n" "Language: it\n" @@ -465,6 +465,9 @@ msgstr "Giochi con quattro mazzi" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "Matrice" @@ -612,6 +615,9 @@ msgstr "Tipo Puzzle" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/po/pl_pysol.po b/po/pl_pysol.po index b17df6ab..504cc06c 100644 --- a/po/pl_pysol.po +++ b/po/pl_pysol.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-10 10:19-0500\n" -"PO-Revision-Date: 2021-12-28 13:51-0500\n" +"PO-Revision-Date: 2022-01-01 23:21-0500\n" "Last-Translator: Jerzy Trzeciak \n" "Language-Team: Polish \n" "Language: pl\n" @@ -460,6 +460,9 @@ msgstr "Gry czterotaliowe" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "Matrix" @@ -607,6 +610,9 @@ msgstr "Gry typu Puzzle" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/po/pysol.pot b/po/pysol.pot index f568cddd..321bfc50 100644 --- a/po/pysol.pot +++ b/po/pysol.pot @@ -433,6 +433,9 @@ msgstr "" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "" @@ -578,6 +581,9 @@ msgstr "" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/po/ru_pysol.po b/po/ru_pysol.po index 4955dfbc..58ec41a8 100644 --- a/po/ru_pysol.po +++ b/po/ru_pysol.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-10 10:19-0500\n" -"PO-Revision-Date: 2021-12-28 13:51-0500\n" +"PO-Revision-Date: 2022-01-01 23:21-0500\n" "Last-Translator: Skomoroh \n" "Language-Team: Russian \n" "Language: ru\n" @@ -462,6 +462,9 @@ msgstr "Игры с четырьмя колодами" msgid "Cribbage" msgstr "" +msgid "Lights Out" +msgstr "" + #: pysollib/resource.py:236 msgid "Matrix" msgstr "Мозаика" @@ -609,6 +612,9 @@ msgstr "Пазлы" msgid "Cribbage type" msgstr "" +msgid "Lights Out type" +msgstr "" + msgid "Pegged type" msgstr "" diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py index 2c4fb275..1ae7ac87 100644 --- a/pysollib/gamedb.py +++ b/pysollib/gamedb.py @@ -57,6 +57,7 @@ class GI: GT_BAKERS_DOZEN = 4 GT_BELEAGUERED_CASTLE = 5 GT_CANFIELD = 6 + GT_CRIBBAGE_TYPE = 37 GT_DASHAVATARA_GANJIFA = 7 GT_FAN_TYPE = 8 GT_FORTY_THIEVES = 9 @@ -64,8 +65,10 @@ class GI: GT_GOLF = 11 GT_GYPSY = 12 GT_HANAFUDA = 13 + GT_HANOI = 35 GT_HEXADECK = 14 GT_KLONDIKE = 15 + GT_LIGHTS_OUT = 38 GT_MAHJONGG = 16 GT_MATRIX = 17 GT_MEMORY = 18 @@ -75,20 +78,20 @@ class GI: GT_NAVAGRAHA_GANJIFA = 22 GT_NUMERICA = 23 GT_PAIRING_TYPE = 24 + GT_PEGGED = 36 GT_POKER_TYPE = 25 GT_PUZZLE_TYPE = 26 GT_RAGLAN = 27 GT_ROW_TYPE = 28 + GT_SHISEN_SHO = 34 GT_SIMPLE_TYPE = 29 GT_SPIDER = 30 GT_TAROCK = 31 GT_TERRACE = 32 GT_YUKON = 33 - GT_SHISEN_SHO = 34 - GT_HANOI = 35 - GT_PEGGED = 36 - GT_CRIBBAGE_TYPE = 37 + GT_CUSTOM = 40 + # extra flags GT_BETA = 1 << 12 # beta version of game driver GT_CHILDREN = 1 << 13 # *not used* @@ -139,6 +142,7 @@ class GI: GT_CRIBBAGE_TYPE: n_("Cribbage"), GT_HEXADECK: n_("Hex A Deck"), + GT_LIGHTS_OUT: n_("Lights Out"), GT_MATRIX: n_("Matrix"), GT_MEMORY: n_("Memory"), GT_PEGGED: n_("Pegged"), @@ -246,6 +250,8 @@ class GI: lambda gi, gt=GT_CRIBBAGE_TYPE: gi.si.game_type == gt), (n_("Hex A Deck type"), lambda gi, gt=GT_HEXADECK: gi.si.game_type == gt), + (n_("Lights Out type"), + lambda gi, gt=GT_LIGHTS_OUT: gi.si.game_type == gt), (n_("Matrix type"), lambda gi, gt=GT_MATRIX: gi.si.game_type == gt), (n_("Memory type"), lambda gi, gt=GT_MEMORY: gi.si.game_type == gt), (n_("Pegged type"), lambda gi, gt=GT_PEGGED: gi.si.game_type == gt), @@ -521,7 +527,7 @@ class GI: ('fc-2.12', tuple(range(774, 811)) + (16681,) + tuple(range(22217, 22219))), ('fc-2.14', tuple(range(811, 827))), - ('fc-2.16', tuple(range(827, 849))) + ('fc-2.16', tuple(range(827, 849)) + tuple(range(22400, 22407))) ) # deprecated - the correct way is to or a GI.GT_XXX flag diff --git a/pysollib/games/special/__init__.py b/pysollib/games/special/__init__.py index da8e0144..ab0d54f8 100644 --- a/pysollib/games/special/__init__.py +++ b/pysollib/games/special/__init__.py @@ -22,6 +22,7 @@ # ---------------------------------------------------------------------------## from . import cribbage # noqa: F401 from . import hanoi # noqa: F401 +from . import lightsout # noqa: F401 from . import memory # noqa: F401 from . import pegged # noqa: F401 from . import poker # noqa: F401 diff --git a/pysollib/games/special/lightsout.py b/pysollib/games/special/lightsout.py new file mode 100644 index 00000000..32b18178 --- /dev/null +++ b/pysollib/games/special/lightsout.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# -*- mode: python; coding: utf-8; -*- +# ---------------------------------------------------------------------------## +# +# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 2003 Mt. Hood Playing Card Co. +# Copyright (C) 2005-2009 Skomoroh +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# ---------------------------------------------------------------------------## + +import math + +from pysollib.game import Game +from pysollib.gamedb import GI, GameInfo, registerGame +from pysollib.layout import Layout +from pysollib.mfxutil import kwdefault +from pysollib.settings import TOOLKIT +from pysollib.stack import \ + InitialDealTalonStack, \ + OpenStack +from pysollib.util import ANY_RANK + +# ************************************************************************ +# * Matrix Row Stack +# ************************************************************************ + + +class LightsOut_RowStack(OpenStack): + + def __init__(self, x, y, game, **cap): + kwdefault(cap, max_move=1, max_accept=1, max_cards=1, + base_rank=ANY_RANK) + OpenStack.__init__(self, x, y, game, **cap) + + def canFlipCard(self): + return 0 + + def canDropCards(self, stacks): + return (None, 0) + + def cancelDrag(self, event=None): + if event is None: + self._stopDrag() + + def _findCard(self, event): + # we need to override this because the shade may be hiding + # the tile (from Tk's stacking view) + return len(self.cards) - 1 + + def clickHandler(self, event): + self.playFlipMove() + + def playFlipMove(self, sound=True, animation=False): + rows = int(math.sqrt(self.game.gameinfo.ncards)) + + playSpace = self.id + + if playSpace % rows != rows - 1: + self.game.s.rows[playSpace + 1].flipMove() + + if playSpace % rows != 0: + self.game.s.rows[playSpace - 1].flipMove() + + if playSpace + rows < rows ** 2: + self.game.s.rows[playSpace + rows].flipMove() + + if playSpace - rows >= 0: + self.game.s.rows[playSpace - rows].flipMove() + + return OpenStack.playFlipMove(self, sound, animation) + + +# Talon that can deal randomly flipped cards. +class LightsOut_Talon(InitialDealTalonStack): + + def dealToStacks(self, stacks, flip=1, reverse=0, frames=-1): + if not self.cards or not stacks: + return 0 + assert len(self.cards) >= len(stacks) + old_state = self.game.enterState(self.game.S_DEAL) + if reverse: + stacks = list(stacks) + stacks.reverse() + for r in stacks: + if self.getCard().face_up: + # TODO: This probably needs a refactor. + # For some reason, unless I do TWO flipMoves here, + # the card will act flipped, but not show as flipped + # when dealt. + self.game.flipMove(self) + self.game.flipMove(self) + self.game.moveMove(1, self, r, frames=frames) + self.game.leaveState(old_state) + if TOOLKIT == 'kivy': + self.game.top.waitAnimation() + return len(stacks) + + +# ************************************************************************ +# * Lights Out Game +# ************************************************************************ + +class LightsOut(Game): + + # + # Game layout + # + + def createGame(self): + self.shownCards = tuple() + + l, s = Layout(self), self.s + grid = math.sqrt(self.gameinfo.ncards) + assert grid == int(grid) + grid = int(grid) + + # Set window size + w, h = l.XM * 2 + l.CW * grid, l.YM * 2 + l.CH * grid + self.setSize(w, h) + + # Create rows + for j in range(grid): + x, y = l.XM, l.YM + l.CH * j + for i in range(grid): + s.rows.append(LightsOut_RowStack(x, y, self)) + x = x + l.CW + + # Create talon + x, y = -2 * l.XS, 0 # invisible + s.talon = LightsOut_Talon(x, y, self) + + # Define stack groups + l.defaultStackGroups() + + # + # Game extras + # + + def _shuffleHook(self, cards): + # no shuffling + cards = self._shuffleHookMoveToTop(cards, lambda c: (1, -c.id)) + + cards.reverse() + return cards + + # + # Game over rides + # + + def startGame(self): + assert len(self.s.talon.cards) == self.gameinfo.ncards + + rows = int(math.sqrt(self.gameinfo.ncards)) + + n = 0 + cards = self.s.talon.cards + for card in cards: + if self.random.randint(0, 1) == 1: + card.face_up = not card.face_up + if n % rows != rows - 1: + cards[n + 1].face_up = not cards[n + 1].face_up + + if n % rows != 0: + cards[n - 1].face_up = not cards[n - 1].face_up + + if n + rows < rows ** 2: + cards[n + rows].face_up = not cards[n + rows].face_up + + if n - rows >= 0: + cards[n - rows].face_up = not cards[n - rows].face_up + + n += 1 + + self.startDealSample() + self.s.talon.dealRow(rows=self.s.rows[:self.gameinfo.ncards], + flip=0, frames=3) + + def isGameWon(self): + if self.busy: + return 0 + s = self.s.rows + for r in s: + if r.cards[0].face_up: + return 0 + return 1 + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return ((card1.rank + 1 == card2.rank) or + (card1.rank - 1 == card2.rank)) + + +# ************************************************************************ +# * Register a Matrix game +# ************************************************************************ + +def r(id, short_name, width): + name = short_name + ncards = width ** 2 + gi = GameInfo( + id, LightsOut, name, + GI.GT_LIGHTS_OUT, 1, 0, GI.SL_SKILL, + category=GI.GC_TRUMP_ONLY, short_name=short_name, + suits=(), ranks=(), trumps=list(range(ncards)), + si={"decks": 1, "ncards": ncards}) + gi.ncards = ncards + gi.rules_filename = "lightsout.html" + registerGame(gi) + return gi + + +r(22400, "Lights Out 4x4", 4) +r(22401, "Lights Out 5x5", 5) +r(22402, "Lights Out 6x6", 6) +r(22403, "Lights Out 7x7", 7) +r(22404, "Lights Out 8x8", 8) +r(22405, "Lights Out 9x9", 9) +r(22406, "Lights Out 10x10", 10) + +del r diff --git a/pysollib/games/ultra/matrix.py b/pysollib/games/ultra/matrix.py index 9d4084a8..1f8bb322 100644 --- a/pysollib/games/ultra/matrix.py +++ b/pysollib/games/ultra/matrix.py @@ -131,7 +131,7 @@ class Matrix_RowStack(OpenStack): # * Matrix Game # ************************************************************************ -class Matrix3(Game): +class Matrix(Game): # # Game layout @@ -208,51 +208,15 @@ class Matrix3(Game): (card1.rank - 1 == card2.rank)) -# ************************************************************************ -# * Size variations -# ************************************************************************ - -class Matrix4(Matrix3): - pass - - -class Matrix5(Matrix3): - pass - - -class Matrix6(Matrix3): - pass - - -class Matrix7(Matrix3): - pass - - -class Matrix8(Matrix3): - pass - - -class Matrix9(Matrix3): - pass - - -class Matrix10(Matrix3): - pass - - -class Matrix20(Matrix3): - pass - - # ************************************************************************ # * Register a Matrix game # ************************************************************************ -def r(id, gameclass, short_name): +def r(id, short_name, width): name = short_name - ncards = int(name[:2]) * int(name[:2]) + ncards = width ** 2 gi = GameInfo( - id, gameclass, name, + id, Matrix, name, GI.GT_MATRIX, 1, 0, GI.SL_SKILL, category=GI.GC_TRUMP_ONLY, short_name=short_name, suits=(), ranks=(), trumps=list(range(ncards)), @@ -263,14 +227,14 @@ def r(id, gameclass, short_name): return gi -r(22223, Matrix3, " 3x3 Matrix") -r(22224, Matrix4, " 4x4 Matrix") -r(22225, Matrix5, " 5x5 Matrix") -r(22226, Matrix6, " 6x6 Matrix") -r(22227, Matrix7, " 7x7 Matrix") -r(22228, Matrix8, " 8x8 Matrix") -r(22229, Matrix9, " 9x9 Matrix") -r(22230, Matrix10, "10x10 Matrix") -# r(22240, Matrix20, "20x20 Matrix") +r(22223, "Matrix 3x3", 3) +r(22224, "Matrix 4x4", 4) +r(22225, "Matrix 5x5", 5) +r(22226, "Matrix 6x6", 6) +r(22227, "Matrix 7x7", 7) +r(22228, "Matrix 8x8", 8) +r(22229, "Matrix 9x9", 9) +r(22230, "Matrix 10x10", 10) +# r(22240, "Matrix 20x20", 20) del r diff --git a/scripts/all_games.py b/scripts/all_games.py index 18e0fe09..65640ea8 100755 --- a/scripts/all_games.py +++ b/scripts/all_games.py @@ -62,6 +62,7 @@ GAME_BY_TYPE = { GI.GT_3DECK_TYPE: "Three-Deck game", GI.GT_4DECK_TYPE: "Four-Deck game", + GI.GT_LIGHTS_OUT: "Lights Out", GI.GT_MATRIX: "Matrix", GI.GT_MEMORY: "Memory", GI.GT_POKER_TYPE: "Poker",