diff --git a/data/images/cards/bottoms/trumps-only/bottom02-n.png b/data/images/cards/bottoms/trumps-only/bottom02-n.png new file mode 100644 index 00000000..732d7676 Binary files /dev/null and b/data/images/cards/bottoms/trumps-only/bottom02-n.png differ diff --git a/data/images/cards/bottoms/trumps-only/bottom02.png b/data/images/cards/bottoms/trumps-only/bottom02.png new file mode 100644 index 00000000..732d7676 Binary files /dev/null and b/data/images/cards/bottoms/trumps-only/bottom02.png differ diff --git a/html-src/rules/pegged.html b/html-src/rules/pegged.html index 3f252faa..120a348e 100644 --- a/html-src/rules/pegged.html +++ b/html-src/rules/pegged.html @@ -9,8 +9,9 @@ Remove all but one card.

Rules

-This is a classic puzzle game. Cards are removed by jumping over -neighbour cards, and the space beyond the neighbour must be empty. +This is a classic puzzle game. At the start of the game, one card is +removed. The remaining cards are removed by jumping over neighbour +cards, and the space beyond the neighbour must be empty.

You win when there is only one card left. @@ -19,4 +20,8 @@ You win when there is only one card left. To get awarded for a perfect game the remaining card must be in the position of the initial free space.

+The first card can be removed automatically, or chosen by the +player and removed manually. This is based on an assist level +option that can be set in the Options menu. +

Autodrop and Quickplay are disabled for this game. diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py index 3bfe2a8e..1cb19776 100644 --- a/pysollib/gamedb.py +++ b/pysollib/gamedb.py @@ -485,7 +485,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, 839))) + ('fc-2.16', tuple(range(827, 841))) ) # deprecated - the correct way is to or a GI.GT_XXX flag diff --git a/pysollib/games/special/pegged.py b/pysollib/games/special/pegged.py index b24fbd5a..481e2098 100644 --- a/pysollib/games/special/pegged.py +++ b/pysollib/games/special/pegged.py @@ -63,7 +63,21 @@ class Pegged_RowStack(ReserveStack): def canDropCards(self, stacks): return (None, 0) + def clickHandler(self, event): + game = self.game + for row in game.s.rows: + if len(row.cards) < 1: + return ReserveStack.clickHandler(self, event) + self.game.emptyStack = self.id + self.game.playSample("drop", priority=200) + self.playMoveMove(1, self.game.s.foundations[0]) + return True + def moveMove(self, ncards, to_stack, frames=-1, shadow=-1): + if type(to_stack) is Pegged_Foundation: + return ReserveStack.moveMove(self, ncards, to_stack, frames=frames, + shadow=shadow) + other_stack = to_stack._getMiddleStack(self) old_state = self.game.enterState(self.game.S_FILL) f = self.game.s.foundations[0] @@ -89,6 +103,16 @@ class Pegged_RowStack(ReserveStack): clone.pos = self.pos +class Pegged_Foundation(AbstractFoundationStack): + + def acceptsCards(self, from_stack, cards): + game = self.game + for row in game.s.rows: + if len(row.cards) < 1: + return False + return True + + # ************************************************************************ # * Pegged # ************************************************************************ @@ -100,19 +124,20 @@ class Pegged(Game): ROWS = (3, 5, 7, 7, 7, 5, 3) EMPTY_STACK_ID = -1 + GAME_VERSION = 2 + # # game layout # def createGame(self): + self.emptyStack = self.EMPTY_STACK_ID # create layout l, s = Layout(self), self.s # set window - n = m = max(self.ROWS) - if self.ROWS[0] == m or self.ROWS[-1] == m: - n = n + 1 - self.setSize(l.XM + n*l.XS, l.YM + len(self.ROWS)*l.YS) + m = max(self.ROWS) + self.setSize(l.XM + m * l.XS, l.YM + len(self.ROWS) * l.YS) # game extras 1) self.map = {} @@ -121,17 +146,17 @@ class Pegged(Game): for i in range(len(self.ROWS)): r = self.ROWS[i] for j in range(r): - d = m - r + 2*j - x, y = l.XM + d*l.XS//2, l.YM + i*l.YS + d = m - r + 2 * j + x, y = l.XM + d * l.XS // 2, l.YM + i * l.YS stack = Pegged_RowStack(x, y, self) - stack.pos = (d, 2*i) + stack.pos = (d, 2 * i) # print stack.id, stack.pos s.rows.append(stack) self.map[stack.pos] = stack - x, y = self.width - l.XS, l.YM + x, y = self.getInvisibleCoords() s.foundations.append( - AbstractFoundationStack( - x, y, self, ANY_SUIT, max_move=0, max_accept=0, + Pegged_Foundation( + x, y, self, ANY_SUIT, max_move=0, max_cards=self.gameinfo.ncards)) l.createText(s.foundations[0], "s") y = self.height - l.YS @@ -160,13 +185,14 @@ class Pegged(Game): card.showBack(unhide=0) def startGame(self): - n = len(self.cards) - len(self.s.rows) + 1 - if n > 0: - self.moveMove(n, self.s.talon, self.s.internals[0], frames=0) self.startDealSample() rows = list(self.s.rows[:]) - rows.remove(rows[self.EMPTY_STACK_ID]) + if self.app.opt.pegged_auto_remove: + rows.remove(rows[self.EMPTY_STACK_ID]) self.s.talon.dealRow(rows=rows, frames=4) + if len(self.s.talon.cards) > 0: + self.moveMove(len(self.s.talon.cards), self.s.talon, + self.s.foundations[0], frames=0) assert len(self.s.talon.cards) == 0 def isGameWon(self): @@ -178,13 +204,23 @@ class Pegged(Game): def getAutoStacks(self, event=None): return ((), (), ()) + def _restoreGameHook(self, game): + self.emptyStack = game.loadinfo.dval.get('EmptyStack') + + def _loadGameHook(self, p): + self.loadinfo.addattr(dval=p.load()) + + def _saveGameHook(self, p): + dval = {'EmptyStack': self.emptyStack} + p.dump(dval) + # Pegged special: check for a perfect game def getWinStatus(self): won, status, updated = Game.getWinStatus(self) if status == 2: stacks = [r for r in self.s.rows if r.cards] assert len(stacks) == 1 - if stacks[0].id != self.EMPTY_STACK_ID: + if stacks[0].id != self.emptyStack: # not perfect return won, 1, self.U_WON return won, status, updated @@ -213,6 +249,19 @@ class PeggedCross2(Pegged): ROWS = (3, 3, 3, 9, 9, 9, 3, 3, 3) +# The Continental Diamond layout - this one is commented +# because it's not known to be possible to have a perfect +# game, though it is winnable. +# class PeggedDiamond(Pegged): +# EMPTY_STACK_ID = 6 +# ROWS = (1, 3, 5, 7, 9, 7, 5, 3, 1) + + +class PeggedDiamond(Pegged): + EMPTY_STACK_ID = 12 + ROWS = (1, 3, 5, 7, 7, 5, 3, 1) + + class Pegged6x6(Pegged): EMPTY_STACK_ID = 14 ROWS = (6, 6, 6, 6, 6, 6) @@ -236,6 +285,11 @@ class PeggedTriangle2(PeggedTriangle1): ROWS = (1, 2, 3, 4, 5, 6) +class PeggedStar(PeggedTriangle1): + EMPTY_STACK_ID = 0 + ROWS = (1, 4, 3, 4, 1) + + # ************************************************************************ # * register the games # ************************************************************************ @@ -244,7 +298,6 @@ def r(id, gameclass, name): ncards = 0 for n in gameclass.ROWS: ncards += n - ncards -= 1 gi = GameInfo(id, gameclass, name, GI.GT_PEGGED, 1, 0, GI.SL_SKILL, category=GI.GC_TRUMP_ONLY, @@ -262,4 +315,6 @@ r(183, Pegged6x6, "Pegged 6x6") r(184, Pegged7x7, "Pegged 7x7") r(210, PeggedTriangle1, "Pegged Triangle 1") r(211, PeggedTriangle2, "Pegged Triangle 2") +r(839, PeggedDiamond, "Pegged Diamond") +r(840, PeggedStar, "Pegged Star") del r diff --git a/pysollib/kivy/menubar.py b/pysollib/kivy/menubar.py index 730e88d6..2835bebf 100644 --- a/pysollib/kivy/menubar.py +++ b/pysollib/kivy/menubar.py @@ -594,6 +594,11 @@ class OptionsMenuDialog(LMenuDialog): self.menubar.tkopt.accordion_deal_all, self.menubar.mOptAccordionDealAll) + self.addCheckNode(tv, rg, + _('Auto-remove first card (in Pegged games)'), + self.menubar.tkopt.accordion_deal_all, + self.menubar.mOptPeggedAutoRemove) + # submenu.add_separator() # ------------------------------------------- @@ -1253,6 +1258,7 @@ class PysolMenubarTk: mahjongg_show_removed=BooleanVar(), shisen_show_hint=BooleanVar(), accordion_deal_all=BooleanVar(), + pegged_auto_remove=BooleanVar(), sound=BooleanVar(), sound_sample_volume=IntVar(), sound_music_volume=IntVar(), @@ -1315,6 +1321,7 @@ class PysolMenubarTk: tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed) tkopt.shisen_show_hint.set(opt.shisen_show_hint) tkopt.accordion_deal_all.set(opt.accordion_deal_all) + tkopt.pegged_auto_remove.set(opt.pegged_auto_remove) tkopt.sound.set(opt.sound) tkopt.sound_sample_volume.set(opt.sound_sample_volume) tkopt.sound_music_volume.set(opt.sound_music_volume) @@ -2118,6 +2125,12 @@ the next time you restart the %(app)s""") % {'app': TITLE}) self.app.opt.accordion_deal_all = self.tkopt.accordion_deal_all.get() # self.game.updateMenus() + def mOptPeggedAutoRemove(self, *args): + if self._cancelDrag(break_pause=False): + return + self.app.opt.pegged_auto_remove = self.tkopt.pegged_auto_remove.get() + # self.game.updateMenus() + def mOptCardset(self, *event): if self._cancelDrag(break_pause=False): return diff --git a/pysollib/options.py b/pysollib/options.py index 3bd1ad2e..ac850686 100644 --- a/pysollib/options.py +++ b/pysollib/options.py @@ -227,6 +227,7 @@ class Options: ('shisen_show_hint', 'bool'), ('shisen_show_matching', 'bool'), ('accordion_deal_all', 'bool'), + ('pegged_auto_remove', 'bool'), ('animations', 'int'), ('redeal_animation', 'bool'), ('win_animation', 'bool'), @@ -311,6 +312,7 @@ class Options: self.mahjongg_show_removed = False self.mahjongg_create_solvable = 2 # 0 - none, 1 - easy, 2 - hard self.accordion_deal_all = True + self.pegged_auto_remove = True if TOOLKIT == 'kivy': self.mahjongg_create_solvable = 1 # 0 - none, 1 - easy, 2 - hard self.shisen_show_hint = True diff --git a/pysollib/ui/tktile/menubar.py b/pysollib/ui/tktile/menubar.py index 06389013..5f12110c 100644 --- a/pysollib/ui/tktile/menubar.py +++ b/pysollib/ui/tktile/menubar.py @@ -169,6 +169,7 @@ class PysolMenubarTkCommon: mahjongg_show_removed=tkinter.BooleanVar(), shisen_show_hint=tkinter.BooleanVar(), accordion_deal_all=tkinter.BooleanVar(), + pegged_auto_remove=tkinter.BooleanVar(), sound=tkinter.BooleanVar(), auto_scale=tkinter.BooleanVar(), preserve_aspect_ratio=tkinter.BooleanVar(), @@ -224,6 +225,7 @@ class PysolMenubarTkCommon: tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed) tkopt.shisen_show_hint.set(opt.shisen_show_hint) tkopt.accordion_deal_all.set(opt.accordion_deal_all) + tkopt.pegged_auto_remove.set(opt.pegged_auto_remove) tkopt.sound.set(opt.sound) tkopt.auto_scale.set(opt.auto_scale) tkopt.preserve_aspect_ratio.set(opt.preserve_aspect_ratio) @@ -526,6 +528,10 @@ class PysolMenubarTkCommon: label=n_("&Deal all cards (in Accordion type games)"), variable=self.tkopt.accordion_deal_all, command=self.mOptAccordionDealAll) + submenu.add_checkbutton( + label=n_("A&uto-remove first card (in Pegged games)"), + variable=self.tkopt.pegged_auto_remove, + command=self.mOptPeggedAutoRemove) menu.add_separator() label = n_("&Sound...") menu.add_command( @@ -1472,6 +1478,12 @@ Unsupported game for import. self.app.opt.accordion_deal_all = self.tkopt.accordion_deal_all.get() # self.game.updateMenus() + def mOptPeggedAutoRemove(self, *args): + if self._cancelDrag(break_pause=False): + return + self.app.opt.pegged_auto_remove = self.tkopt.pegged_auto_remove.get() + # self.game.updateMenus() + def _updateCardSize(self): geom = (self.app.canvas.winfo_width(), self.app.canvas.winfo_height())