1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00
PySolFC/pysollib/games/ultra/hanafuda_common.py
Frédéric Brière 0abb801203 Define canMoveCards() for Hanafuda sequence stacks
Any stack of Hanafuda cards is always deemed movable, even if it is out
of sequence.  The effect can easily be seen a game such as Firecracker,
where:

 - Any stack can be dragged as a whole (but not released)
 - "Highlight piles" will highlight everything
 - Asking for a hint will ignore most valid moves

This is due to Hanafuda_SequenceStack lacking a canMoveCards() method.
2019-07-28 10:15:38 +03:00

466 lines
15 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/>.
#
# ---------------------------------------------------------------------------##
import math
from pysollib.game import Game
from pysollib.hint import DefaultHint
from pysollib.mfxutil import kwdefault
from pysollib.mygettext import _
from pysollib.stack import \
AbstractFoundationStack, \
OpenStack, \
ReserveStack, \
cardsFaceUp, \
isRankSequence
from pysollib.util import ANY_RANK, ANY_SUIT
class AbstractFlowerGame(Game):
SUITS = (_("Pine"), _("Plum"), _("Cherry"), _("Wisteria"),
_("Iris"), _("Peony"), _("Bush Clover"), _("Eularia"),
_("Chrysanthemum"), _("Maple"), _("Willow"), _("Paulownia"))
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return ((card1.suit == card2.suit) and
((card1.rank + 1 == card2.rank) or
(card1.rank - 1 == card2.rank)))
class Queue_Hint(DefaultHint):
pass
# ************************************************************************
# * Flower Foundation Stacks
# ***********************************************************************/
class Flower_FoundationStack(AbstractFoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, max_cards=12, max_move=0, base_rank=ANY_RANK,
base_suit=ANY_SUIT)
AbstractFoundationStack.__init__(self, x, y, game, suit, **cap)
def updateText(self):
AbstractFoundationStack.updateText(self)
self.game.updateText()
def isHanafudaSequence(self, s, strictness=1):
for i in range(len(s) - 1):
if s[i].suit != s[i + 1].suit:
return 0
if s[i].suit == 10 or strictness:
a, b = s[i].rank, s[i + 1].rank
else:
a, b = self.swapTrashCards(s[i], s[i + 1])
if a + 1 != b:
return 0
return cardsFaceUp(s)
def swapTrashCards(self, carda, cardb):
a, b = carda.rank, cardb.rank
if a == 3 and b == 2:
a, b = 2, 3
elif a == 1 and b == 3:
b = 2
return a, b
def getBaseCard(self):
return '' # FIXME
def getHelp(self):
return '' # FIXME
class Hanafuda_SS_FoundationStack(Flower_FoundationStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 3
return self.isHanafudaSequence([cards[0], stackcards[-1]])
def getBottomImage(self):
return self.game.app.images.getSuitBottom(self.cap.suit)
class FlowerClock_Foundation(Flower_FoundationStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 3
if not stackcards[-1].suit == cards[0].suit:
return 0
return stackcards[-1].rank == cards[0].rank + 1
class Gaji_Foundation(Flower_FoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, max_move=1, min_cards=1, max_accept=1,
base_suit=ANY_SUIT)
Flower_FoundationStack.__init__(self, x, y, game, suit, **cap)
self.CARD_YOFFSET = self.game.app.images.CARD_YOFFSET
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
return ((((stackcards[-1].suit + 1) % 12) == cards[0].suit) and
(stackcards[-1].rank == cards[0].rank))
def getBottomImage(self):
return self.game.app.images.getLetter(self.cap.base_rank)
class Pagoda_Foundation(Flower_FoundationStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 3
a, b = stackcards[-1].rank, cards[0].rank
if len(stackcards) < 4:
return a - 1 == b
elif len(stackcards) > 4:
return a + 1 == b
else:
return a == b
def getBottomImage(self):
return self.game.app.images.getSuitBottom(self.cap.suit)
class MatsuKiri_Foundation(Flower_FoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, max_move=0, max_cards=48, max_accept=4, min_accept=4)
AbstractFoundationStack.__init__(self, x, y, game, suit, **cap)
self.CARD_YOFFSET = self.game.app.images.CARDH // 10
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
if not self.isHanafudaSequence(cards, 0):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].suit == 0
return stackcards[-1].suit + 1 == cards[0].suit
# def getBottomImage(self):
# return self.game.app.images.getBraidBottom()
class GreatWall_FoundationStack(Flower_FoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, max_cards=48, max_move=1, min_accept=1, max_accept=1)
Flower_FoundationStack.__init__(self, x, y, game, suit, **cap)
self.CARD_YOFFSET = self.game.app.images.CARDH // 20
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if stackcards:
return ((stackcards[-1].suit + 1) % 12 == cards[0].suit and
cards[0].rank == self.cap.base_rank)
else:
return cards[0].suit == 0
def getBottomImage(self):
return self.game.app.images.getLetter(self.cap.base_rank)
class FourWinds_Foundation(Flower_FoundationStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not (cards[0].rank == self.cap.base_rank):
return 0
if not stackcards:
return (cards[0].suit == 0)
else:
return (cards[0].suit == stackcards[-1].suit + 1)
# def getBottomImage(self):
# return self.game.app.images.getLetter(self.cap.base_rank)
class Queue_Foundation(AbstractFoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, mod=12, dir=0, base_suit=ANY_SUIT, max_move=0)
AbstractFoundationStack.__init__(self, x, y, game, suit, **cap)
def acceptsCards(self, from_stack, cards):
if not AbstractFoundationStack.acceptsCards(self, from_stack, cards):
return 0
if not self.cards:
return cards[0].suit == self.game.base_card.suit
stack_dir = self.game.getFoundationDir()
if stack_dir == 0:
card_dir = (cards[0].suit - self.cards[-1].suit) % 12
return card_dir in (1, 11)
else:
return (self.cards[-1].suit + stack_dir) % 12 == cards[0].suit
def getBottomImage(self):
return self.game.app.images.getLetter(self.cap.base_rank)
# ************************************************************************
# * Flower Row Stacks
# ***********************************************************************/
class Flower_OpenStack(OpenStack):
def __init__(self, x, y, game, yoffset, **cap):
kwdefault(cap, max_move=99, max_cards=99, max_accept=99, base_rank=0,
dir=1)
OpenStack.__init__(self, x, y, game, **cap)
self.CARD_YOFFSET = yoffset
def isHanafudaSequence(self, cards, strictness=1):
c1 = cards[0]
for c2 in cards[1:]:
if c1.suit != c2.suit:
return 0
if c1.suit == 10 or strictness:
a, b = c1.rank, c2.rank
else:
a, b = self.swapTrashCards(c1, c2)
if a + self.cap.dir != b:
return 0
c1 = c2
return 1
def swapTrashCards(self, carda, cardb):
a, b = carda.rank, cardb.rank
if a == 3 and b == 2:
a, b = 2, 3
elif a == 1 and b == 3:
b = 2
return a, b
class Hanafuda_SequenceStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards) \
or not self.isHanafudaSequence(cards):
return 0
stackcards = self.cards
if not len(stackcards):
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
return self.isHanafudaSequence([stackcards[-1], cards[0]])
def canMoveCards(self, cards):
return self.basicCanMoveCards(cards) and self.isHanafudaSequence(cards)
class Oonsoo_SequenceStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards) \
or not self.isHanafudaSequence(cards, 0):
return 0
stackcards = self.cards
if not len(stackcards):
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
return self.isHanafudaSequence([stackcards[-1], cards[0]], 0)
def canMoveCards(self, cards):
return self.basicCanMoveCards(cards) and self.isHanafudaSequence(cards, 0)
class FlowerClock_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not len(stackcards):
return 1
return stackcards[-1].rank + 1 == cards[0].rank
class Gaji_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if ((not len(stackcards)) or
((stackcards[-1].suit == 10) and (stackcards[-1].rank == 3)) or
((cards[0].suit == 10) and (cards[0].rank == 3))):
return 1
elif stackcards[-1].suit != cards[0].suit:
return 0
a, b = self.swapTrashCards(stackcards[-1], cards[0])
return a + 1 == b
class Matsukiri_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 0
if cards[0].suit != stackcards[-1].suit:
return 0
if stackcards[-1].suit == 10 or self.game.Strictness:
a, b = stackcards[-1].rank, cards[0].rank
else:
a, b = self.swapTrashCards(stackcards[-1], cards[0])
return a + 1 == b
def canDropCards(self, stacks):
pile = self.getPile()
if not pile or len(pile) <= 3:
return (None, 0)
f = self.game.s.foundations[0]
if not f.cards:
suit = 0
else:
suit = f.cards[-1].suit + 1
if not pile[-1].suit == suit or \
not self.isHanafudaSequence(pile[-4:], 0):
return (None, 0)
return (f, 4)
class Samuri_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 0
return stackcards[-1].suit == cards[0].suit and \
stackcards[-1].rank + 1 == cards[0].rank
class GreatWall_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if not stackcards:
return cards[0].rank == 0
if cards[0].rank == stackcards[-1].rank:
return stackcards[-1].suit == (cards[0].suit + 1) % 12
a, b = self.swapTrashCards(stackcards[-1], cards[0])
return a + 1 == b
class FourWinds_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
stackcards = self.cards
if len(cards) - 1 or len(stackcards) >= 3:
return 0
if not stackcards:
return 1
return ((cards[0].rank == stackcards[-1].rank) and
(cards[0].suit == stackcards[-1].suit - 1))
def getBottomImage(self):
return self.game.app.images.getReserveBottom()
class Queue_BraidStack(OpenStack):
def __init__(self, x, y, game, yoffset):
OpenStack.__init__(self, x, y, game)
self.CARD_YOFFSET = int(self.game.app.images.CARD_YOFFSET * yoffset)
# use a sine wave for the x offsets
# compensate for card width
offset = self.game.app.images.CARDW / 1.7
self.CARD_XOFFSET = []
j = 1
for i in range(20):
self.CARD_XOFFSET.append(int(math.sin(j) * offset))
j = j + .9
class Queue_RowStack(ReserveStack):
def fillStack(self):
if not self.cards and self.game.s.braid.cards:
self.game.moveMove(1, self.game.s.braid, self)
def getBottomImage(self):
return self.game.app.images.getBraidBottom()
class Queue_ReserveStack(ReserveStack):
def acceptsCards(self, from_stack, cards):
if from_stack is self.game.s.braid or from_stack in self.game.s.rows:
return 0
return ReserveStack.acceptsCards(self, from_stack, cards)
def getBottomImage(self):
return self.game.app.images.getTalonBottom()
class JapaneseGarden_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if (not self.basicAcceptsCards(from_stack, cards) or
from_stack not in self.game.s.rows):
return 0
stackcards = self.cards
if not len(stackcards):
return 1
return stackcards[-1].rank + 1 == cards[0].rank
class HanafudaRK_RowStack(Flower_OpenStack):
def acceptsCards(self, from_stack, cards):
if (not self.basicAcceptsCards(from_stack, cards) or
not isRankSequence(cards, dir=1)):
return 0
stackcards = self.cards
if not len(stackcards):
return 1
return stackcards[-1].rank + 1 == cards[0].rank