mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
Revamped Pegged game and added manual first card removal option.
This commit is contained in:
parent
940a5d584e
commit
7af8fde4c0
8 changed files with 106 additions and 19 deletions
BIN
data/images/cards/bottoms/trumps-only/bottom02-n.png
Normal file
BIN
data/images/cards/bottoms/trumps-only/bottom02-n.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 B |
BIN
data/images/cards/bottoms/trumps-only/bottom02.png
Normal file
BIN
data/images/cards/bottoms/trumps-only/bottom02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 B |
|
@ -9,8 +9,9 @@ Remove all but one card.
|
|||
|
||||
<h3>Rules</h3>
|
||||
<p>
|
||||
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.
|
||||
<p>
|
||||
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.
|
||||
<p>
|
||||
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.
|
||||
<p>
|
||||
<i>Autodrop</i> and <i>Quickplay</i> are disabled for this game.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Add table
Reference in a new issue