Skip to content

Commit

Permalink
Gin: players can draw from discard or pass on first turn (#47)
Browse files Browse the repository at this point in the history
* gin: first turn allow to pass

* OK

* OK

* PK

* OK

* PK

* OK

* OK

* hm

* OK
  • Loading branch information
cdrappi authored May 31, 2023
1 parent 997b0a8 commit 725d606
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 38 deletions.
68 changes: 59 additions & 9 deletions card_utils/games/gin/game_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(
p1_hand: List[str],
p2_hand: List[str],
turn: RummyTurn,
first_turn: RummyTurn,
public_hud: Optional[Dict[str, RummyHud]] = None,
last_draw=None,
last_draw_from_discard=None,
Expand Down Expand Up @@ -67,6 +68,7 @@ def __init__(
self.p2_hand = p2_hand

self.turn = turn
self.first_turn = first_turn

self.last_draw = last_draw
self.last_draw_from_discard = last_draw_from_discard
Expand All @@ -90,14 +92,18 @@ def __init__(
self.underknock_bonus = underknock_bonus
self.gin_bonus = gin_bonus

def draw_card(self, from_discard):
def draw_card(self, from_discard: bool):
"""draw card from top of deck or discard to player's hand
:param from_discard: (bool)
:return: (str) card drawn
"""

if self.turn not in {RummyTurn.P1_DRAWS, RummyTurn.P2_DRAWS}:
if not (
self.turn.is_draw()
or self.turn.is_first_draw()
or self.turn.is_draw_from_deck()
):
raise ValueError(
"Cannot draw: it is not the player's turn to draw"
)
Expand Down Expand Up @@ -149,7 +155,12 @@ def draw_card(self, from_discard):

hand = self.p1_hand if self.turn.p1() else self.p2_hand
deadwood = self.get_deadwood(hand)
self.turn = self.advance_turn(self.turn, deadwood)
self.turn = self.advance_turn(
current=self.turn,
from_discard=from_discard,
first_turn=self.first_turn,
deadwood=deadwood, # arbitrary
)
self.turns += 1
self.last_draw = card_drawn
self.last_draw_from_discard = from_discard
Expand All @@ -169,10 +180,31 @@ def sort_hand(hand: List[str]) -> List[str]:
raise NotImplementedError("sort_hand not implemented")

@staticmethod
def advance_turn(turn: RummyTurn, deadwood: int) -> RummyTurn:
def advance_turn(
current: RummyTurn,
from_discard: bool,
first_turn: RummyTurn,
deadwood: int,
) -> RummyTurn:
raise NotImplementedError("advance_turn not implemented")

def discard_card(self, card) -> None:
def first_turn_pass(self) -> None:
if not self.turn.is_first_draw():
raise ValueError("Cannot pass: it is not the first turn")
self.turns += 1
self.turn = self.advance_turn(
current=self.turn,
from_discard=False, # arbitrary
first_turn=self.first_turn,
deadwood=10, # arbitrary
)
if self.turn == RummyTurn.P1_DRAWS_FROM_DECK:
# they must draw from the deck now
self.draw_card(from_discard=False)
elif self.turn == RummyTurn.P2_DRAWS_FROM_DECK:
self.draw_card(from_discard=False)

def discard_card(self, card: Card) -> None:
"""discard card from player's hand to discard pile
:param card: (card)
Expand Down Expand Up @@ -217,7 +249,12 @@ def discard_card(self, card) -> None:
p1_deadwood = 0 if is_p1 else opp_deadwood
p2_deadwood = opp_deadwood if is_p1 else 0
self.end_game(RummyEndGame.GIN, p1_deadwood, p2_deadwood)
self.turn = self.advance_turn(self.turn, deadwood)
self.turn = self.advance_turn(
current=self.turn,
from_discard=False, # arbitrary
first_turn=self.first_turn, # arbitrary
deadwood=deadwood,
)

# add discard to HUD
if self.discard:
Expand All @@ -240,7 +277,12 @@ def decide_knock(
)

if not knocks:
self.turn = self.advance_turn(self.turn, 10) # 10 is arbitrary
self.turn = self.advance_turn(
current=self.turn,
from_discard=False, # arbitrary
first_turn=self.first_turn, # arbitrary
deadwood=10, # arbitrary
)
return

if self.turn == RummyTurn.P1_MAY_KNOCK:
Expand Down Expand Up @@ -298,9 +340,17 @@ def _add_to_hand(self, card_drawn):
:param card_drawn: (str)
:return: None
"""
if self.turn == RummyTurn.P1_DRAWS:
if self.turn in {
RummyTurn.P1_DRAWS,
RummyTurn.P1_DRAWS_FIRST,
RummyTurn.P1_DRAWS_FROM_DECK,
}:
self.p1_hand.append(card_drawn)
elif self.turn == RummyTurn.P2_DRAWS:
elif self.turn in {
RummyTurn.P2_DRAWS,
RummyTurn.P2_DRAWS_FIRST,
RummyTurn.P2_DRAWS_FROM_DECK,
}:
self.p2_hand.append(card_drawn)
else:
raise Exception(
Expand Down
41 changes: 34 additions & 7 deletions card_utils/games/gin/ricky/game_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from card_utils.deck.utils import Card
from card_utils.games.gin.game_state import AbstractGinGameState
from card_utils.games.gin.ricky.utils import hand_points, sort_hand
from card_utils.games.gin.utils import RummyTurn
from card_utils.games.gin.utils import RummyAction, RummyTurn

GIN_RICKY_CARDS_DEALT = 7
GIN_RICKY_END_CARDS_IN_DECK = 0
Expand All @@ -18,6 +18,7 @@ def __init__(
p1_hand,
p2_hand,
turn: RummyTurn,
first_turn: RummyTurn,
public_hud=None,
last_draw=None,
last_draw_from_discard=None,
Expand All @@ -33,6 +34,7 @@ def __init__(
p1_hand=p1_hand,
p2_hand=p2_hand,
turn=turn,
first_turn=first_turn,
public_hud=public_hud,
last_draw=last_draw,
last_draw_from_discard=last_draw_from_discard,
Expand Down Expand Up @@ -60,18 +62,43 @@ def sort_hand(hand: List[str]) -> List[str]:
return sort_hand(hand)

@staticmethod
def advance_turn(turn: RummyTurn, deadwood: int) -> RummyTurn:
def advance_turn(
current: RummyTurn,
from_discard: bool,
first_turn: RummyTurn,
deadwood: int,
) -> RummyTurn:
"""set next player to draw
:return: None
"""
if turn == RummyTurn.P1_DRAWS:
if current == RummyTurn.P1_DRAWS_FIRST:
if from_discard:
# they picked up a card, so they discard now
return RummyTurn.P1_DISCARDS
elif first_turn == RummyTurn.P2_DRAWS_FIRST:
# they didn't pick up a card, so the other player draws
return RummyTurn.P2_DRAWS_FROM_DECK
else:
return RummyTurn.P2_DRAWS_FIRST
elif current == RummyTurn.P2_DRAWS_FIRST:
if from_discard:
return RummyTurn.P2_DISCARDS
elif first_turn == RummyTurn.P1_DRAWS_FIRST:
return RummyTurn.P1_DRAWS_FROM_DECK
else:
return RummyTurn.P1_DRAWS_FIRST
elif current == RummyTurn.P1_DRAWS_FROM_DECK:
return RummyTurn.P1_DISCARDS
elif turn == RummyTurn.P1_DISCARDS:
elif current == RummyTurn.P2_DRAWS_FROM_DECK:
return RummyTurn.P2_DISCARDS
elif current == RummyTurn.P1_DRAWS:
return RummyTurn.P1_DISCARDS
elif current == RummyTurn.P1_DISCARDS:
return RummyTurn.P2_DRAWS
elif turn == RummyTurn.P2_DRAWS:
elif current == RummyTurn.P2_DRAWS:
return RummyTurn.P2_DISCARDS
elif turn == RummyTurn.P2_DISCARDS:
elif current == RummyTurn.P2_DISCARDS:
return RummyTurn.P1_DRAWS

raise ValueError(f"Invalid Gin Ricky turn: {turn}")
raise ValueError(f"Invalid Gin Ricky turn: {current}")
15 changes: 6 additions & 9 deletions card_utils/games/gin/ricky/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

from card_utils import deck
from card_utils.deck.utils import suit_partition
from card_utils.games.gin.utils import get_sets, new_game, rank_straights
from card_utils.games.gin.utils import (
get_sets,
new_game,
rank_straights,
sort_cards_by_rank,
)


def deal_new_game():
Expand Down Expand Up @@ -111,14 +116,6 @@ def sum_points_by_ranks(hand):
return sum(deck.rank_to_value[r] for r, _ in hand)


def sort_cards_by_rank(cards: Iterable[str]):
"""
:param cards: ([str])
:return: ([str])
"""
return sorted(cards, key=lambda c: deck.rank_to_value[c[0]])


def sort_hand(hand) -> List[str]:
"""
:param hand: ([str])
Expand Down
45 changes: 37 additions & 8 deletions card_utils/games/gin/rummy/game_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(
p1_hand: List[str],
p2_hand: List[str],
turn: RummyTurn,
first_turn: RummyTurn,
public_hud=None,
last_draw=None,
last_draw_from_discard=None,
Expand All @@ -37,6 +38,7 @@ def __init__(
p1_hand=p1_hand,
p2_hand=p2_hand,
turn=turn,
first_turn=first_turn,
public_hud=public_hud,
last_draw=last_draw,
last_draw_from_discard=last_draw_from_discard,
Expand Down Expand Up @@ -69,32 +71,59 @@ def sort_hand(hand: List[str]) -> List[str]:
return [c for m in best_melds for c in m] + unmelded

@staticmethod
def advance_turn(turn: RummyTurn, deadwood: int) -> RummyTurn:
def advance_turn(
current: RummyTurn,
from_discard: bool,
first_turn: RummyTurn,
deadwood: int,
) -> RummyTurn:
"""set next player to draw
:return: None
"""
if turn == RummyTurn.P1_DRAWS:
if current == RummyTurn.P1_DRAWS_FIRST:
if from_discard:
# they picked up a card, so they discard now
return RummyTurn.P1_DISCARDS
elif first_turn == RummyTurn.P2_DRAWS_FIRST:
# they didn't pick up a card, so the other player draws
return RummyTurn.P2_DRAWS_FROM_DECK
else:
return RummyTurn.P2_DRAWS_FIRST
elif current == RummyTurn.P2_DRAWS_FIRST:
if from_discard:
return RummyTurn.P2_DISCARDS
elif first_turn == RummyTurn.P1_DRAWS_FIRST:
return RummyTurn.P1_DRAWS_FROM_DECK
else:
return RummyTurn.P1_DRAWS_FIRST

elif current == RummyTurn.P1_DRAWS_FROM_DECK:
return RummyTurn.P1_DISCARDS
elif current == RummyTurn.P2_DRAWS_FROM_DECK:
return RummyTurn.P2_DISCARDS

elif current == RummyTurn.P1_DRAWS:
return RummyTurn.P1_DISCARDS
elif turn == RummyTurn.P1_DISCARDS:
elif current == RummyTurn.P1_DISCARDS:
if deadwood <= 10:
return RummyTurn.P1_MAY_KNOCK
else:
return RummyTurn.P2_DRAWS
elif turn == RummyTurn.P1_MAY_KNOCK:
elif current == RummyTurn.P1_MAY_KNOCK:
return RummyTurn.P2_DRAWS

elif turn == RummyTurn.P2_DRAWS:
elif current == RummyTurn.P2_DRAWS:
return RummyTurn.P2_DISCARDS
elif turn == RummyTurn.P2_DISCARDS:
elif current == RummyTurn.P2_DISCARDS:
if deadwood <= 10:
return RummyTurn.P2_MAY_KNOCK
else:
return RummyTurn.P1_DRAWS
elif turn == RummyTurn.P2_MAY_KNOCK:
elif current == RummyTurn.P2_MAY_KNOCK:
return RummyTurn.P1_DRAWS

raise ValueError(f"Invalid Gin Rummy turn: {turn}")
raise ValueError(f"Invalid Gin Rummy turn: {current}")

def get_knock_candidates(self) -> List[Tuple[int, List[List[Card]]]]:
"""
Expand Down
6 changes: 3 additions & 3 deletions card_utils/games/gin/rummy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

from card_utils.deck import rank_to_value, value_to_rank
from card_utils.deck.utils import Card, rank_partition, suit_partition
from card_utils.games.gin.ricky.utils import rank_straights, sort_cards_by_rank
from card_utils.games.gin.utils import get_sets, new_game
from card_utils.games.gin.ricky.utils import rank_straights
from card_utils.games.gin.utils import get_sets, new_game, sort_cards_by_rank


def deal_new_game():
Expand Down Expand Up @@ -71,7 +71,7 @@ def get_candidate_melds(
if unique_cards == total_cards:
um_cards = sort_cards_by_rank(hand_set - melded_cards)
deadwood = get_deadwood(um_cards)
melds_list = [list(m) for m in melds]
melds_list = [sort_cards_by_rank(m) for m in melds]
if deadwood == 0 and stop_on_gin:
# they made gin, so return early
return [(0, melds_list, [])]
Expand Down
Loading

0 comments on commit 725d606

Please sign in to comment.