1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00
PySolFC/pysollib/games/pasdedeux.py
2017-04-19 12:03:44 +03:00

218 lines
7.1 KiB
Python

#!/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 <http://www.gnu.org/licenses/>.
#
# ---------------------------------------------------------------------------##
__all__ = []
# imports
import sys
# PySol imports
from pysollib.gamedb import registerGame, GameInfo, GI
from pysollib.util import *
from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
# ************************************************************************
# *
# ************************************************************************
class PasDeDeux_Hint(AbstractHint):
# FIXME: this is very simple
def getDistance(self, stack, card):
d = 0
if card.rank != stack.id % 13:
d = d + 1
if card.suit != stack.id / 13:
d = d + 1
return d
def computeHints(self):
# find the single stack that can currently move a card
rows = []
for r in self.game.s.rows:
if r.canMoveCards(r.cards):
rows.append(r)
break
# for each stack
for r in rows:
r1_d = self.getDistance(r, r.cards[-1])
column, row = r.id % 13, r.id / 13
stack_ids = range(column, 52, 13) + range(13*row, 13*row+13)
for i in stack_ids:
t = self.game.s.rows[i]
if t is r:
continue
assert t.acceptsCards(r, r.cards)
t1_d = self.getDistance(t, t.cards[-1])
# compute distances after swap
r2_d = self.getDistance(t, r.cards[-1])
t2_d = self.getDistance(r, t.cards[-1])
#
rw, tw = 3, 2
if self.game.s.talon.round >= 2:
rw, tw = 4, 2
c = self.game.cards[t.cards[-1].id - 52]
if 1 and c in self.game.s.waste.cards:
rw = rw - 1
#
score = int(((rw*r1_d + tw*t1_d) - (rw*r2_d + tw*t2_d)) * 1000)
if score > 0:
self.addHint(score, 1, r, t)
# ************************************************************************
# * Pas de Deux
# ************************************************************************
class PasDeDeux_Waste(WasteStack):
def canFlipCard(self):
return False
class PasDeDeux_RowStack(ReserveStack):
def canMoveCards(self, cards):
if not ReserveStack.canMoveCards(self, cards):
return False
if not self.game.s.waste.cards:
return False
c = self.game.s.waste.cards[-1]
return c.face_up and cards[0].suit == c.suit and cards[0].rank == c.rank
def acceptsCards(self, from_stack, cards):
if not ReserveStack.acceptsCards(self, from_stack, cards):
return False
# must be neighbours
return self.game.isNeighbour(from_stack, self)
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
assert ncards == 1 and to_stack in self.game.s.rows
assert len(to_stack.cards) == 1
self._swapPairMove(ncards, to_stack, frames=-1, shadow=0)
if self.game.s.talon.canDealCards():
self.game.s.talon.dealCards()
else:
# mark game as finished by turning the Waste face down
assert self.game.s.waste.cards[-1].face_up
self.game.flipMove(self.game.s.waste)
def _swapPairMove(self, n, other_stack, frames=-1, shadow=-1):
game = self.game
old_state = game.enterState(game.S_FILL)
swap = game.s.internals[0]
game.moveMove(n, self, swap, frames=0)
game.moveMove(n, other_stack, self, frames=frames, shadow=shadow)
game.moveMove(n, swap, other_stack, frames=0)
game.leaveState(old_state)
def getBottomImage(self):
suit = self.id / 13
return self.game.app.images.getSuitBottom(suit)
def quickPlayHandler(self, event, from_stacks=None, to_stacks=None):
# find the single stack that can currently move a card
for r in self.game.s.rows:
if r.canMoveCards(r.cards):
if self.acceptsCards(r, r.cards):
r.playMoveMove(len(r.cards), self)
return 1
break
return 0
class PasDeDeux(Game):
Hint_Class = PasDeDeux_Hint
#
# game layout
#
def createGame(self):
# create layout
l, s = Layout(self, card_x_space=4), self.s
# set window
self.setSize(l.XM + 13*l.XS, l.YM + 5*l.YS)
# create stacks
for i in range(4):
for j in range(13):
x, y, = l.XM + j*l.XS, l.YM + i*l.YS
s.rows.append(PasDeDeux_RowStack(x, y, self, max_accept=1, max_cards=2))
x, y = self.width - 2*l.XS, self.height - l.YS
s.talon = WasteTalonStack(x, y, self, max_rounds=2)
l.createText(s.talon, "se")
l.createRoundText(s.talon, 'ne')
x -= l.XS
s.waste = PasDeDeux_Waste(x, y, self, max_move=0)
l.createText(s.waste, "sw")
s.internals.append(InvisibleStack(self)) # for _swapPairMove()
# define stack-groups
l.defaultStackGroups()
#
# game overrides
#
def shuffle(self):
self.shuffleSeparateDecks()
def startGame(self):
self.startDealSample()
self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() # deal first card to WasteStack
def getAutoStacks(self, event=None):
return ((), (), (self.sg.dropstacks))
def isGameWon(self):
for r in self.s.rows:
if len(r.cards) != 1:
return False
c = r.cards[-1]
if c.suit != r.id / 13 or c.rank != r.id % 13:
return False
return True
#
# game extras
#
def isNeighbour(self, stack1, stack2):
column1, row1 = stack1.id % 13, stack1.id / 13
column2, row2 = stack2.id % 13, stack2.id / 13
return column1 == column2 or row1 == row2
def getHighlightPilesStacks(self):
# Pas de Deux special: highlight all moveable cards
return ((self.s.rows, 1),)
# register the game
registerGame(GameInfo(153, PasDeDeux, "Pas de Deux",
GI.GT_MONTANA | GI.GT_SEPARATE_DECKS, 2, 1, GI.SL_MOSTLY_SKILL))