:
First, we will write functions to validate key play types in COMP10001-Bo,
starting with a play to a "build pile". Here, we are validating whether a given
card can be legally placed on a given build pile, irrespective of the source of
that card. We will come back to consider whether it is legal to play cards in
different ways, using this function.
Write the function comp10001bo_match_build(play_card,build_pile) that
takes a string argument play_card (a 2-chracter string made up of the card
value and suit, as described below) and a list of strings build_pile (each
representing a card, also in the form. of a 2-character string; the ordering of
the cards in the list represents their ordering within the pile, with the first card
being at the bottom of the pile and the last card being at the top of the pile).
The function should return True if the card can be legally placed on the given
build pile and False otherwise. Recall that for build piles:
a. the first card must be either a 2 ('2') or a King ('K');
b. the pile must follow colour (i.e. be all red or all black);
c. adjacent cards in the pile must be adjacent in value (e.g. a '3'can be
placed on a '2' or '4' but not a '5') and cannot loop around
from '2' to 'K' and vice versa; and
d. an Ace ('A') of appropriate colour can be placed on a non-empty build
pile at any time to complete it (with the other way to complete a build
pile being to place a '2' or 'K' on a non-empty pile).
You may assume that all card arguments are valid, and that the pile is also
valid.
:
Next, we will write a function to validate a play to a "discard pile", to
determine whether a given card can be legally placed on a given discard pile,
irrespective of the source of that card. We will come back to consider
whether it is legal to play cards from different sources, using this function.
Write the
function comp10001bo_match_discard(play_card,discard_pile, player_no, to_
player_no, from_hand=True):that takes the following arguments:
play_card: a string (a 2-chracter string made up of the card value and
suit, as per the previous question) describing the card the player is
attempting to place on the discard pile
discard_pile: a list of strings representing the discard pile the card is to
be placed on (each string represents a card in the form. of a
2-character string; the ordering of the cards in the list represents their
ordering within the pile, with the first card being at the bottom of the
pile and the last card being at the top of the pile)
player_no: an integer representing the player number of the player
attempting the play, with value 0-3, representing Players 0-3,
respectively.
to_player_no: an integer representing the player number for the player
who owns the discard pile in question
from_hand: a bool indicating whether the card is being played from
the player's hand (True) or the stockpile/another discard pile (False),
set to True by default
The function should return one of the following values:
0 if the play is invalid (i.e. it is not possible to play the card in that way)
1 if the play is a valid non-turn-ending play (i.e. it is a valid play to a
non-empty discard pile)
2 if the play is a valid turn-ending play (i.e. it is an illegal play to a
non-empty discard pile OR a valid play to an empty discard pile of that
player, to start a new discard pile)
Recall that for discard piles:
a. a discard pile can be started with any card, but a given player can only
start one of their own discard piles (not an empty discard pile of
another player);
b. cards in the pile must alternate in colour (e.g. if the first card is red, the
next card must be black, then red again, etc.);
c. adjacent cards in the pile must be adjacent in value (e.g. a '3'can be
placed on a '2' or '4' but not a '5') and can loop around
from '2' to 'K' and vice versa (i.e. a 'K' can be placed on a '2' and vice
versa); and
d. Aces ('A') cannot be placed on discard piles.
You may assume that all arguments are valid in value, and that the pile is also
valid.
:
Time to put the pieces together, in validating a play in the context of the full
game context, with full specification of the play type. We have provided
reference versions
of comp10001bo_match_build and comp10001bo_match_discard to help in
testing your function, although for the final version of your player that will be
submitted to the tournament, you will need to have implemented all three
parts of the project yourself.
Write the
function comp10001bo_is_valid_play(play,player_no, hand, stockpiles, discard
_piles,build_piles) that takes the following arguments:
play: a 3-tuple specifying the nature of the play, with the following
elements:
o play_type: an integer, where 0 indicates a play from the
hand, 1 indicates a play from a discard pile, 2 indicates a play
from the player's stockpile, and 3 indicates that no play is
possible
o source: a specification of where the card is to be taken from; for
a play from the hand, this is the card to be played (a string); for
a play from a discard pile, this is a 2-tuple made up of the card
and the identity of the discard pile (itself a 2-tuple, made up of
the player number and discard pile number, e.g. (2, 1) indicates
Player 2, discard pile 1); for a play from the player's stockpile,
this is the card to be played (a string); and in the instance of
there being no valid play, the value should be None
o destination: a specification of where the card is to be played to,
in the form. of a 2-tuple indicating the destination pile type (0 =
build pile; 1 = discard pile), and the identity of the pile (an
integer 0-3 in the case of a build pile, and a 2-tuple of the
player number and discard pile number in the case of discard
pile, e.g. (0, 3)indicates Player 0 discard pile 3); in the instance
that there is no valid play, the value should be (None, None)
player_no: an integer representing the player number of the player
attempting the play (of value 0-3)
hand: a list of cards held by the current player
stockpiles: a 4-tuple describing the content of the stockpiles for each
of the four players, each of which is in the form. of a 2-tuple, made up
of the top card (a string) and the number of cards in the stockpile
(including the top card); note that the stockpiles are indexed according
to player number
discard_piles: a 4-tuple describing the content of the discard piles for
each of the four players, each of which is in the form. of a 4-tuple of
lists of cards (with the final card in the list being the top card of that
pile); note that the discard piles are indexed according to player
number
build_piles: a 4-tuple of lists of cards describing the content of the
build piles, each of which is in the form. of a list of cards (with the final
card in the list being the top card of that pile)
The function should return one of the following values:
0 if the play is invalid
1 if the play is a valid non-turn-ending play (recalling that a
turn-ending play takes the form. of an illegal play to a non-empty
discard pile)
2 if the play is a valid turn-ending play
3 if there is no possible play (legal or illegal)
Note that if a build pile is completed, it will automatically be removed and
shuffled in with the draw pile. Similarly, you can assume that the 5 plays for
the turn have not been exhausted.
Here are some example calls to the comp10001bo_is_valid_playfunction:
>>> # NON-FINAL VALID (from hand to build pile 0)
>>> comp10001bo_is_valid_play((0, '2C', (0, 0)), 0, ['2C', 'AS', '9D', '0D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), ([], [], [], []))
1
>>> # INVALID: doesn't hold card
>>> comp10001bo_is_valid_play((0, '2C', (0, 0)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), ([], [], [], []))
0
>>> # INVALID: can't play to build pile 0 (can't start with 3)
>>> comp10001bo_is_valid_play((0, '3C', (0, 0)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), ([], [], [], []))
0
>>> # NON-FINAL VALID (from hand to non-empty build pile 0)
>>> comp10001bo_is_valid_play((0, '3C', (0, 0)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), (['2S'], [], [], []))
1
>>> # NON-FINAL VALID (from stockpile to empty build pile 1)
>>> comp10001bo_is_valid_play((2, '2C', (0, 1)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), (['2S'], [], [], []))
1
>>> # INVALID: attempt to play card that is not top card of own stockpile
>>> comp10001bo_is_valid_play((2, '2H', (0, 1)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), (['2S'], [], [], []))
0
>>> # INVALID: attempt to play card that is not top card of own stockpile
(despite being top card of someone else's stockpile)
>>> comp10001bo_is_valid_play((2, '2H', (0, 1)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('2C', 8), ('2H', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), (['2S'], [], [], []))
0
>>> # NON-FINAL VALID (from stockpile to non-empty build pile)
>>> comp10001bo_is_valid_play((2, 'QC', (0, 1)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('QC', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), ([], ['KS'], [], []))
1
>>> # NON-FINAL VALID (from stockpile to *empty* build pile 1)
>>> comp10001bo_is_valid_play((2, 'KC', (0, 1)), 0, ['3C', 'AS', '9D', '0D', '0S'],
(('KC', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), ([], [], [], []), ([], [], [], []),
([], [], [], [])), ([], [], [], []))
1
>>> # NON-FINAL VALID (from discard pile to empty build pile 0)
>>> comp10001bo_is_valid_play((1, ('2C', (1, 0)), (0, 1)), 0, ['3C', 'AS', '9D',
'0D', '0S'], (('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), (['3C', '2C'], [],
[], []), ([], [], [], []), ([], [], [], [])), (['2S'], [], [], []))
1
>>> # INVALID: attempt to access non-top card from discard stack 0 of
player 1
>>> comp10001bo_is_valid_play((1, ('3C', (1, 0)), (0, 1)), 0, ['3C', 'AS', '9D',
'0D', '0S'], (('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), (['3C', '2C'], [],
[], []), ([], [], [], []), ([], [], [], [])), (['2S'], [], [], []))
0
>>> # INVALID: can't place 2C (from discard stack 0 of Player 1) on 2S
(build stack 0)
>>> comp10001bo_is_valid_play((1, ('2C', (1, 0)), (0, 0)), 0, ['3C', 'AS', '9D',
'0D', '0S'], (('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), (['3C', '2C'], [],
[], []), ([], [], [], []), ([], [], [], [])), (['2S'], [], [], []))
0
>>> # FINAL VALID: can place 9D (from hand) on 5S (discard stack 0 of
Player 0), but final play for turn
>>> comp10001bo_is_valid_play((0, '9D', (1, (0, 0))), 0, ['AS', '9D', '0D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), ((['5S'], [], [], []), ([], [], [], []), ([], [], [],
[]), ([], [], [], [])), ([], [], [], []))
2
>>> # INVALID: can make a number of different plays
>>> comp10001bo_is_valid_play((3, None, (None, None)), 0, ['AS', '9D', '0S'],
(('9C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), ((['5S'], [], [], []), ([], [], [], []), ([], [], [],
[]), ([], [], [], [])), ([], [], [], []))
0
>>> # NO_PLAY: no move possible (yes, it's an impossible game state, but it
proves a point)
>>> comp10001bo_is_valid_play((3, None, (None, None)), 0, [], (('9C', 8),
('0D', 8), ('3H', 8), ('KD', 8)), ((['5S'], [], [], []), ([], [], [], []), ([], [], [], []), ([], [],
[], [])), ([], [], [], []))
3
>>> # INVALID (attempt to move card from discard pile back to same
discard pile)
>>> comp10001bo_is_valid_play((1, ('2C', (1, 0)), (1, (1, 0))), 0, ['3C', 'AS',
'9D', '0D', '0S'], (('2C', 8), ('0D', 8), ('3H', 8), ('KD', 8)), (([], [], [], []), (['3C',
'2C'], [], [], []), ([], [], [], []), ([], [], [], [])), (['2S'], [], [], []))
0
:
Finally now, implement your game-playing strategy, optionally making use of
your own implementations
of comp10001bo_match_build, comp10001bo_match_discard,
and comp10001bo_is_valid_play (in which case, copy your code from the
previous questions into common.py along with any constants and helper
functions).
Write the
function comp10001bo_play(player_no, hand,stockpiles, discard_piles, build_
piles, play_history)that takes the following arguments:
player_no: an integer representing the player number of the player
attempting the play (of value 0-3)
hand: a list of cards held by the current player
stockpiles: a 4-tuple describing the content of the stockpiles for each
of the four players, each of which is in the form. of a 2-tuple, made up
of the top card (a string) and the number of cards in the stockpile
(including the top card); note that the stockpiles are indexed according
to player number
discard_piles: a 4-tuple describing the content of the discard piles for
each of the four players, each of which is in the form. of a 4-tuple of
lists of cards (with the final card in the list being the top card of that
pile); note that the discard piles are indexed according to player
number
build_piles: a 4-tuple of lists of cards describing the content of the
build piles, each of which is in the form. of a list of cards (with the final
card in the list being the top card of that pile)
play_history: a list of 2-tuples specifying the sequence of plays to this
point in the game, where each 2-tuple is made up of: (1) the ID of the
player making the move, and (2) the play, based on the same structure
as comp10001_is_valid_play, namely:
o play_type: an integer, where 0 indicates a play from the
hand, 1 indicates a play from a discard pile, 2 indicates a play
from the player's stockpile, and 3 indicates that no play is
possible
o source: a specification of where the card is to be taken from; for
a play from the hand, this is the card to be played (a string); for
a play from a discard pile, this is a 2-tuple made up of the card
and the identity of the discard pile (itself a 2-tuple, made up of
the player number and discard pile number, e.g. (2, 1) indicates
Player 2, discard pile 1); for a play from the player's stockpile,
this is the card to be played (a string); and in the instance of
there being no valid play, the value should be None
o destination: a specification of where the card is to be played to,
in the form. of a 2-tuple indicating the destination pile type (0 =
build pile; 1 = discard pile), and the identity of the pile (an
integer 0-3 in the case of a build pile, and a 2-tuple of the
player number and discard pile number in the case of discard
pile, e.g. (0, 3)indicates Player 0 build pile 3); in the instance that
there is no valid play, the value should be (None, None)
The function should return a 3-tuple stipulating the play you wish to make,
based on the same play format as for play_history above.
Note that your hand will automatically be replenished to 5 cards before each
turn, and that your function will only be called in you haven't exhausted your
plays for the turn/explicitly terminated your turn. Additionally, if a build pile
is completed, it will automatically be removed and shuffled in with the draw
pile.
Here are some example calls to the comp10001bo_play function (noting that
these require that there is a single possible play possible for the given game
state; to simplify the test cases, we have created an impossible game state,
namely that all hands are empty and the draw pile is also empty):
>>> # no possible play
>>> comp10001bo_play(0, [], (('7H', 7), ('3C', 8), ('3H', 8), ('KD', 8)), ((['7H'],
[], [], []), ([], [], [], []), ([], [], [], []), ([], [], [], [])), (['2C'], [], [], []), [(0, (2, '2C',
(0, 0)))])
(3, None, (None, None))
>>> # play from stockpile to build pile
>>> comp10001bo_play(1, [], (('7H', 7), ('3C', 8), ('3H', 8), ('KD', 8)), ((['7H'],
[], [], []), ([], [], [], []), ([], [], [], []), ([], [], [], [])), (['2C'], [], [], []), [(0, (2, '2C',
(0, 0))), (0, (3, None, (None, None))), (1, (2, '3C', (0, 0)))])
(2, '3C', (0, 0))
>>> # play from stockpile to build pile
>>> comp10001bo_play(1, [], (('7H', 7), ('4S', 7), ('3H', 8), ('KD', 8)), ((['7H'],
[], [], []), ([], [], [], []), ([], [], [], []), ([], [], [], [])), (['2C', '3C'], [], [], []), [(0, (2,
'2C', (0, 0))), (0, (3, None, (None, None))), (1, (2, '3C', (0, 0)))])
(2, '4S', (0, 0))
>>> # play from stockpile to build pile, with example play
>>> comp10001bo_play(1, [], (('7H', 7), ('3S', 6), ('3H', 8), ('KD', 8)), ((['7H'],
[], [], []), ([], [], [], []), ([], [], [], []), ([], [], [], [])), (['2C', '3C', '4S'], [], [], []), [(0,
(2, '2C', (0, 0))), (0, (3, None, (None, None))), (1, (2, '3C', (0, 0))), (1, (2, '4S',
(0, 0)))])
(2, '3S', (0, 0))
>>> # no valid play possible
>>> comp10001bo_play(1, [], (('7H', 7), ('0D', 5), ('3H', 8), ('KD', 8)), ((['7H'],
[], [], []), ([], [], [], []), ([], [], [], []), ([], [], [], [])), (['2C', '3C', '4S', '3S'], [], [], []),
[(0, (2, '2C', (0, 0))), (0, (3, None, (None, None))), (1, (2, '3C', (0, 0))), (1, (2,
'4S', (0, 0))), (1, (2, '3S', (0, 0)))])
(3, None, (None, None))