Package: hundredandten
Hundred and TenA python package to provide an engine for playing the game Hundred and Ten.fromhundredandtenimportHundredAndTengame=HundredAndTen()How To PlayHundred and Ten is a trick-taking game structured into rounds of five tricks each that continue until one player has reached a score of 110 or above.The game is played with a normal 52 card deck and includes one Joker for a total deck of 53 cards.DealerFor each round, one player is considered the dealer. In an over-the-table game, this player would be responsible for shuffling the deck and dealing out the cards. With this package, that is all handled. However, the dealer still retains some additional privileges that go along with the role:Bidding begins to the left of the dealer. This means that the dealer is always the last to place a bid and can make their decision with the context of other players' bids.Normally, to steal a bid, players must bidabovethe current amount. The dealer, however, can steal a bid for the current amount.The position of dealer moves to the current dealer's left at the end of the round, except with conditions described inpassing.BiddingBefore the round begins, players are dealt a hand of five cards, which they will use to determine if they wish to "bid" any points. The highest bidder will decide which suit is trumps for the round. However, if the highest bidder fails to earn at least the amount of points they bid, they instead lose that amount (seescoring).Bidding begins to the left of thedealerand continues clockwise around the table until either all players have passed or a single bidder remains.PassingIf players do not believe their hand is strong enough to bid, they can pass on their turn. If all players pass, the round ends.The same player will remain dealer after a round when all players pass, for a maximum of three rounds. If the current dealer has been the dealer for the last three rounds, thendealerwill pass to the left.OptionsPlayers may only bid one of the following options:15202530Shoot the Moon (see:Scoring)Stealing a BidWhen a player places a bid, other players in the round must bid above this amount (unless they are thedealer) or pass. Bidding above (or at, see:dealer) the current highest bid is called Stealing the Bid and bidding continues around the table until all but one player has passed.Selecting TrumpThe highest bidder selects the trump suit for the round. Once the trump is selected, all players at the table may choose to discard any or all of their current hand and have their hand refilled from the deck. This begins with the dealer, and continues clockwise until all players have had the opportunity to discard.TricksEach round consists of five tricks. Each player contributes one card to a trick. When all players have played a card, the trick ends and a winner is determined (seeWinning a Trick).The first trick begins with the player to the left of the highest bidder; play continues clockwise until the trick is complete. Subsequent tricks will begin with the winner of the previous trick.Trump CardsA card is a trump card if it is the suit selected by the highest bidder.Additionally, the Ace of Hearts and the Joker are both considered trump cards regardless of the suit chosen by the bidder. This is important to remember forwinning a trickandbleeding.Winning a TrickA trick is won by the highest value card in the trick. Trump cards will always beat non-trump cards.See below for the card hierarchy for each suit, when it is trump. Recall the additionaltrump cardsthat will not necessarily be of the selected suit. Note that red suits and black suits follow the same trump hierarchy with the exception of their number cards. Lower value black number cards will beat higher value black number cards.RankHeartsDiamondsSpadesClubsHighest5JackJokerAceAce of HeartsAceKingQueen102938476674839Lowest210If no trump card is played, the suit of the first played card is considered trump for the trick. It will not follow the trump order listed above, though. Instead, it will follow a normal Ace-high card hierarchy. The only exception is that lower value black number cards still beat higher value black number cards. The table below describes the full order.RankHeartsDiamondsSpadesClubsHighestAceKingQueenJack10293847556674839Lowest210BleedingIf the first card in a trick is a trump card, the trick is "bleeding". This means that all playersmustplay a trump card if they have one in their hand. If they do not have one in their hand, they may play whatever card they wish.ScoringEach round (which consists of five tricks) normally offers thirty points in total to be won by players. The trick that was won with the highest value card is worth ten points. The remaining four tricks are worth five points each.If the tricks won by the highest bidder do not equal or exceed the amount they bid, they will instead lose the amount of their bid. The tricks they won will not offset their loss. For example, if a player began the round at zero points, bid fifteen, and won ten points, that player will end the round at negative fifteen points.Additionally, if the highest bidder decided toShoot the Moon, they will earn sixty points if they won every trick that round. Otherwise, they will lose sixty points.Only the highest bidder can lose points.WinningThe game ends if, after a round is scored, one or more players has a total score greater than or equal to 110.If multiple players are above 110, the winner is determined as follows:If the current highest bidder is among the players above 110, the highest bidder wins.Otherwise, the player who would have reached 110 first within the round wins.PlayersAll players in the game must have a unique string identifier. They may also have a list of applicable roles, but, for most use cases, the engine should manage those roles itself.RolesGameRole.ORGANIZER: This person is organizing the game. They have no more inherent permissions than any other player within the engine, but they can be tracked if consumers want to give them additional permissions.GameRole.PLAYER: This person is attached to the game and will be playing if or once the game begins.GameRole.INVITEE: This person is attached to the game as an invitee and will be able to join the game even if the accessibility is set to private.RoundRole.DEALER: This player is acting as the dealer for the current round. This role is only attached to persons/players at the round level, not the game level. This role describes the invividual as the dealer and does offer inherent play differences as described in therules.RoundRole.PRE_PASSED: This player has elected to pass before their bidding turn. When play reaches them, they will perform a pass action automatically.Inviting, Joining, and LeavingWhile not necessary for play, this engine provides support use cases involving a form of player lobby before the start of the game.HundredAndTen.inviteAny player already in the game can invite any other player.game=HundredAndTen()game.join('player_identifier')# without joining first, this next line would errorgame.invite('player_identifier','other_player')HundredAndTen.joinJoining a public game (the default) is as easy as callingjoin.game=HundredAndTen()# game is public and there are fewer than 4 players# they will join as the identified playergame.join('player_identifier')Joining a private game requires an invite before the player will be able to joinjoin.# be sure to initialize private games with one player# otherwise they will not be joinablegame=HundredAndTen(Group([Person('organizer')]),accessibility=Accessibility.PRIVATE)game.invite('organizer','player_identifier')# game is private, so this would error without the above invitegame.join('player_identifier')HundredAndTen.leaveTo leave a game, simply callleave.game=HundredAndTen()game.join('player_identifier_1')game.leave('player_identifier_1')Starting a GameHundredAndTen.start_gameBegin play by callingstart_gameon theHundredAndTeninstance.game=HundredAndTen()game.join('player_1')game.join('player_2')game.join('player_3')game.join('player_4')game.start_game()Once a game has begun, players can no longer join or leave the game.Determining the State of a GameHundredAndTen.statusThe status of the game can be one of the following valuesGameStatus.WAITING_FOR_PLAYERS: The game has not yet begun. Players may still join or leave.RoundStatus.BIDDING: Players are bidding in the current round.RoundStatus.COMPLETED_NO_BIDDERS: The current round is complete, but no player submitted a bid. This should be a transitionary state. Any game that reaches this state should immediately begin a new round and enterRoundStatus.BIDDING.RoundStatus.TRUMP_SELECTION: The bidder in the current round is selecting their trump value.RoundStatus.DISCARD: Players in the current round are discarding from their hard to refill.RoundStatus.COMPLETED: The current round is complete with tricks won by the players. This should also be a transitionary state. Any game that reaches this state should either begin a new round and enterRoundStatus.BIDDINGor determine a winner and enterGameStatus.WONGameStatus.WON: The game is complete and a winner has been determined. No further actions are allowed.HundredAndTen.active_round.active_playerThis field will hold the current active player.HundredAndTen.scoresThis field will hold the current score values. In the form of adict[str, int].For example:{'p1':55,'p2':70,'p3':-15,'p4':25}HundredAndTen.winnerThis field will hold the winner of the game, if the game is inGameStatus.WON. Otherwise, it will beNone.Playing a GameOnce a game has begun, theHundredAndTeninstance should only be interacted with through theactmethod. In this manner, players can bid, unpass, select trump, discard cards, or play a card.Eachactcan only be performed by the current active player. If another player attempts to act, it will result in an error.The exception to this is pre-passing. Any player may pass before their turn during the bidding stage. A bid of any other amount will still result in an error.HundredAndTen.acttoBidTo bid, callactwith aBidobject.fromhundredandtenimportHundredAndTen,Bid,BidAmount# set up and start the gamegame.act(Bid('active_player_identifier',BidAmount.FIFTEEN))To pre-pass, a non-active player must bid a pass.fromhundredandtenimportHundredAndTen,Bid,BidAmount# set up and start the gamegame.act(Bid('non_active_player_identifier',BidAmount.PASS))# when 'non_active_player_identifier' becomes the active player, they will passHundredAndTen.acttoUnpassIf a player has prepassed, but would now like toBida non-zero amount, they shouldUnpassto avoid automatically passing on their turn.fromhundredandtenimportHundredAndTen,Bid,BidAmount,Unpass# set up and start the game# pass as the non active playergame.act(Unpass('non_active_player_identifier'))HundredAndTen.acttoSelectTrumpOnce a player has won the bid, that player must select the trump suit for that round.fromhundredandtenimportHundredAndTen,SelectTrump,SelectableSuit# set up and start the game# select a bidder# any of the four SelectableSuit value can be usedgame.act(SelectTrump('bidder',SelectableSuit.CLUBS))HundredAndTen.acttoDiscardOnce trump has been selected, each player will have the opportunity to discard a portion of their hand and refill with new cards.
This must be done in player order.fromhundredandtenimportHundredAndTen,Discard# set up and start the game# select a bidder# select trumpgame.act(Discard('active_player',game.active_round.active_player.hand[1:3]))HundredAndTen.acttoPlayOnce bidding, trump selection, and discard have all occurred. Players will play through tricks.This action will enforce that played cards follow the rule relating to"bleeding".fromhundredandtenimportHundredAndTen,Play# set up and start the game# select a bidder# select trump# discardgame.act(Play('active_player',game.active_round.active_player.hand[0]))Machine PlayHundredAndTen.suggestionAt any state of active play (when the game is notGameStatus.WONorGameStatus.WAITING_FOR_PLAYERS), the game can provide a suggested action.fromhundredandtenimportHundredAndTen# set up and start the gameprint(game.suggestion())# will be a Bid, SelectTrump, Discard, or Play actionHundredAndTen.automateAny player can be automated at any time. But, once automated, a player cannot be unautomated. Automated players will always play the suggested action.fromhundredandtenimportHundredAndTengame=HundredAndTen()game.join('machine_player')game.automate('machine_player')