关键词 > CSSE1001/CSSE7030

CSSE1001/CSSE7030 Fancy Naughts & Crosses Assignment 1 Semester 2, 2023

发布时间:2023-08-15

Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

Fancy Naughts & Crosses

Assignment 1

Semester 2, 2023

CSSE1001/CSSE7030

Due date: 25 August 2023, 15:00 GMT+10

1    Introduction

In this assignment, you will implement a text-based modified naughts and crosses game inspired by this demo. The game is similar to regular naughts and crosses, but di↵ers in that each player has markers of di↵erent sizes. The game is played on a 3 ⇥ 3 grid between two players. Starting with naughts (O), the players take turns placing their markers in cells on the board. A player may place any of their markers on an empty cell, or they may place a larger marker on top of a smaller marker already present on the board. The top-most marker is the one considered to be occupying the cell. The game is over when either:

1. One player wins by filling a row, column, or diagonal with their markers (note that the only marker that counts for the win check is the top-most marker)

2. The game reaches stalemate when no move can be made on the board but no player has met the win condition.

2    Getting Started

Download a1. zip from Blackboard — this archive contains the necessary files to start this as- signment. Once extracted, the a1. zip archive will provide the following files:

a1. py This  is  the  only file  you  will  submit  and is where you write your code. Do  not  make changes to any other files.

constants. py Do  not modify or submit this file, it contains pre-defined constants to use in your assignment. In addition to these, you are encouraged to create your own constants in a1. py where possible.

gameplay/ This folder contains a number of example outputs generated by playing the game using a fully-functional completed solution to this assignment.  The purpose of the files in this folder is to help you understand how the game works, and how output should be formatted.

NOTE: You are not permitted to add any import statements to a1. py. Doing so will result in a deduction of up to 100% of your mark.

3    Gameplay

This  section  provides  an  overview  of gameplay.   Where  prompts  and  outputs  are  not  explicitly mentioned in this section, please see  Section 4  and the example games in the gameplay/ folder provided with this assignment.

The game begins with an empty board, and with both players having all available pieces.  Naught (O) gets to make the first move.  Until the end of the game, the following steps repeat:

1.  The current game state is displayed (including the pieces remaining for each player and the current board state) .

2.  The user is informed whose turn it is to move.

3.  The user is prompted for a move.  At this prompt they may enter a valid move in the format "{row} {column} {size}",  or  they  may  request to see a help message by entering  "h" or "H".   If  the  user  enters  "h" or  "H",  they  should  be  shown  the  help  message  before  being reprompted for a move.  Otherwise,  progress to the next step.

4.  If the move is invalid for any reason, show the user a message to inform them of why their move was invalid  (see Table 1 for all required validity checking and messages for this step), and then return to step 3.  If the move is valid, progress to the next step.

5.  Update the board according to the requested move and display the new game state.

6.  If the  game is not over, return to step  2 .

7.  If  the  game  is  over,  inform  the  users  of  the  outcome  and  prompt  them  for  whether  they would like to play again.  At this prompt, if they enter either  ‘y’ or  ‘Y’, create a new game (i.e.  set up an empty board and give the players all their pieces back) and return to step  1 . If they enter anything other than ‘y’ or ‘Y’, terminate the program gracefully.

Note that we will not test the case where one player cannot make a move and the other player can; that is, you do not need to handle the case that the current player is out of moves to make but the next player would be able to make a move.

4    Implementation

This  section  outlines  the  functions  you  are  required to  implement  in  your  solution  (in  a1. py only) .  You  are  awarded marks for the number of tests passed by your functions when they  are tested independently of one another.  Thus an incomplete assignment with some working functions may well be awarded more marks than a complete assignment with faulty functions.  Your pro- gram must operate  exactly as specified.  In particular, your program’s output must match  exactly with the  expected  output.   Your  program will  be  marked  automatically  so  minor  di↵erences  in output (such as whitespace or casing) will cause tests to fail resulting in a zero mark for that test.

Each function is accompanied with some examples for usage to help you start your own testing. You  should  also  test  your  functions  with  other  values  to  ensure  they  operate  according  to  the descriptions.

The following functions must be implemented in a1. py.  They have been listed in  a rough order of increasing difficulty.  This does not mean that earlier functions are necessarily worth less marks than  later  functions.   It  is  highly  recommended that  you  do  not  begin  work  on  a  later  function

Issue with user input

Constant in constants. py

The move entered is not 5 characters long, or does not contain 3 non-space characters each separated by a space character

INVALID  FORMAT  MESSAGE

The first character is not a valid row on the board

INVALID  ROW  MESSAGE

The second non-space character is not a valid column on the board

INVALID  COLUMN  MESSAGE

The  final  character  is  not  a  valid  piece  size (note that a piece size may still be valid even if the player has already played that piece) . Valid piece sizes are between 1 and whatever value is stored in PIECES  PER  PLAYER inclu- sive.

INVALID  SIZE  MESSAGE

The move is requesting to place a marker the player has already placed, or is requesting to place  a  piece  over  a  marker  of  the  same  or greater size

INVALID  MOVE  MESSAGE

Table 1:  Constants containing the messages to display when invalid user input is entered.  Prece- dence is top down (i.e.  if there are multiple issues with user input, only display the message for the one which occurs first in this table) .

until each of the preceding functions can  at least behave  as  per  the  shown  examples.  You may implement  additional  functions  if  you  think  they  will  help  with  your  logic  or  make  your  code easier to understand.

The type hints in these functions make reference to Board, Pieces, and Move type aliases defined in the provided  a1. py.  A variable of type Board is of type  list[list[str]],  meaning that it is a list where each element is also a list.  The internal lists contain strings.  A variable of type Pieces is of type  list[int] meaning it is a list containing strings.   See example usages of the relevant  functions  for  what  to  expect  in  these  variables.    A  Move is  of  type  tuple[int, int, int], meaning it is a tuple containing exactly 3 integers.

4.1    num hours() -> float

This function should return the number of hours you estimate you spent (or have spent so far) on the assignment,  as a float.  Ensure this function passes the relevant test on  Gradescope  as soon as possible.  If the  Gradescope tests have been released, you must ensure this function passes the relevant test before seeking help regarding Gradescope issues for any of the later functions.  See Section 6.3 for instructions on how to submit your assignment to Gradescope.

The  purpose  of this  function  is  to  enable  you  to  verify  that  you  understand  how  to  submit  to Gradescope as soon as possible, and to allow us to gauge difficulty level of this assignment in order to provide the best possible assistance.  You  will not be marked di↵erently for spending more or less time on the assignment.

4.2    generate initial pieces(num pieces: int) -> Pieces

Return a list of the initial marker sizes, from 1 up to and including num pieces .  You may assume that num pieces will be between 5 and 9 inclusive.  The piece sizes within a single players piece

list can be assumed to be unique throughout this assignment (that is, one player cannot have two pieces of the same size).

Example:

>>> generate_initial_pieces(5)

[1, 2, 3, 4, 5]

>>> generate_initial_pieces(8)

[1, 2, 3, 4, 5, 6, 7, 8]

4.3    initial state() -> Board

Returns a new board where every cell (i.e. row, column position) contains EMPTY.

Example:

>>> initial_state()

[['    ',  '   ',  '   '], ['   ', '   ', '   '], ['   ', '   ', '   ']]

4.4    place piece(board: Board, player: str, pieces available: Pieces,

move: Move ) -> None

This function should place the requested piece at the requested position on the board and remove that piece from the pieces available list. move is a tuple containing (row, column, piece size). That is, move specifies the requested (row, column) position on which to place player’s piece of the requested piece size. The marker placed should be a string of length 2, containing the player marker and piece size respectively. Note that this function does not return anything; its job is to mutate (i.e. change) the board and pieces available.

You may assume that the requested piece size will be present in pieces available.

This function does not need to handle checking for whether the move is valid according to the game rules.

Example:

>>> board = initial_state()

>>> pieces = generate_initial_pieces(6)

>>> board

[['    ',  '   ',  '   '], ['   ', '   ', '   '], ['   ', '   ', '   ']]

>>> pieces

[1, 2, 3, 4, 5, 6]

>>> place_piece(board, NAUGHT, pieces, (1, 1, 3))

>>> board

[['    ',  '    ',  '    '],  ['    ',  'O3',  '    '],  ['    ',  '    ',  '    ']]

>>> pieces

[1, 2, 4, 5, 6]

>>> place_piece(board, NAUGHT, pieces, (1, 1, 1))

>>> board

[['    ',  '    ',  '    '],  ['    ',  'O1',  '    '],  ['    ',  '    ',  '    ']]

>>> pieces

[2, 4, 5, 6]

4.5    print game(board: Board, naught pieces: Pieces, cross pieces: Pieces)

-> None

Displays the game in a user-friendly format. Your output must exactly match the expected for- matting (including whitespace and grammar) in order to receive marks. Ensure when testing with the below examples that your output exactly matches the output shown. The authority on the correct formatting are the Gradescope tests. Ensure your output aligns exactly with the Gradescope sample tests.

Example:

>>> board = initial_state()

>>> naught_pieces = generate_initial_pieces(5)

>>> cross_pieces = generate_initial_pieces(5)

>>> print_game(board, naught_pieces, cross_pieces)

O has: 1, 2, 3, 4, 5

X has: 1, 2, 3, 4, 5

1  2  3

1|   |   |   |

2|   |   |   |

3|   |   |   |

>>> place_piece(board, NAUGHT, naught_pieces, (1, 1, 1))

>>> place_piece(board, CROSS, cross_pieces, (0, 0, 1))

>>> print_game(board, naught_pieces, cross_pieces)

O has: 2, 3, 4, 5

X has: 2, 3, 4, 5

1  2  3

1|X1|   |   |

2|   |O1|   |

3|   |   |   |

4.6    process move(move: str) -> Move | None

Attempts to convert the move string to a tuple of three integers representing the (row, column, piece size) of the move. If the move is invalid, this function should print the relevant message and return None; see the first 4 rows of Table 1 for the relevant messages. Note that this function does not need to handle the final issue described in Table 1. If the move is in the valid format, this function returns a tuple containing 3 integers, representing the row, column, and size of the piece respectively.

If the conversion is successful, this function should return the converted move. Note that, in the string move, the row and column will be 1-indexed, whereas in the returned tuple the row and

column should be 0-indexed.

Example:

>>> process_move('apple')

Invalid move format . Please try again .

>>> process_move('apple orange berry')

Invalid move format . Please try again .

>>> process_move('a o b')

Invalid row . Please try again .

>>> process_move('0 0 1')

Invalid row . Please try again .

>>> process_move('1 0 1')

Invalid column . Please try again .

>>> process_move('1 1 a')

Invalid piece size . Please try again .

>>> process_move('1 1 1')

(0, 0, 1)

>>> process_move('3 3 5')

(2, 2, 5)

>>> result = process_move('apple')

Invalid move format . Please try again .

>>> result

>>> result = process_move('2 2 2')

>>> result

(1, 1, 2)

4.7    get player move() -> Move

Continuously prompts the user for a move until a move is entered in a valid format, and returns information about the final valid move entered as a tuple of integers in the format (row, col- umn, size). This function should handle displaying a help message if the user enters either "h" or "H" at the prompt (and then continue prompting until a valid move is entered). Each time the user enters something in an invalid format, the relevant message should be displayed as per the first four rows of Table 1. Note that the final row does not need to be handled in this function.

Example:

>>> get_player_move()

Enter your move: apple orange banana

Invalid move format . Please try again .

Enter your move: 0 0 0

Invalid row . Please try again .

Enter your move: 1 1 0

Invalid piece size . Please try again .

Enter your move: 1 1 1

(0, 0, 1)

>>> get_player_move()

Enter your move: h

Enter a row, column & piece size in the format: row col size

Enter your move: 2 2 2

(1, 1, 2)

4.8    check move(board: Board, pieces available: Pieces, move: Move) -> bool

Checks whether move is a valid move for the current board according to the game rules. In order to be a valid move, the piece size must be available in the given pieces available list, and the (row, column) position described in move must be empty, or contain a piece of size smaller than that described in move. Returns True if the move is valid and False otherwise.

You may assume within this function that the move is within the bounds of the board and refers to a piece size that would have existed in the initial pieces for the player.

4.9    check win(board: Board) -> str | None

Checks whether the game has been won. If the game has been won, this function should return who won. Otherwise, this function should return None.

Example:

>>> board = [['O1', 'O2', 'O3'], [EMPTY, EMPTY, EMPTY], [EMPTY, EMPTY, EMPTY]]

>>> check_win(board)

'O'

>>> board = initial_state()

>>> check_win(board)

>>> board = [['X1', 'O2', EMPTY], [EMPTY, 'X2', 'O3'], ['O4', EMPTY, 'X8']]

>>> check_win(board)

'X'

4.10    check stalemate(board: Board, naught pieces: Pieces, cross pieces:

Pieces) -> bool

Returns True if there are no more moves that can be made in this game, else False. Note that a player can place a marker on top of one of their own markers, provided the existing marker is smaller than the new marker.

Example:

>>> board = [['X3', 'O3', 'X2'], ['O4', 'X4', 'O2'], ['O5', 'X6', 'O6']]

>>> check_win(board)

>>> check_stalemate(board, [1], [1])

True


>>> check_stalemate(board, [1], [1, 5])

False

4.11    main() -> None

The main function should be called when the file is run, and coordinates the overall gameplay. Section 3 describes how the game should be played.  The main function should utilize other functions you have written. In order to make the main function shorter, you should consider writing extra helper functions. In the provided a1. py, the function definition for main has already been provided, and the if  name     ==  main     : block will ensure that the code in the main function is run when your a1. py file is run. Do not call your main function outside of this block, and do not call any other function outside this block unless you are calling them from within the body of another function. The output from your main function (including prompts) must exactly match the expected output. Running the sample tests will give you a good idea of whether your prompts and other outputs are correct. See the gameplay/ folder provided with this assignment for examples of how the main function should run.

5    CSSE7030 Task:  Changing GRID  SIZE

This section describes an additional task that CSSE7030 students are required to complete for full marks. Students in CSSE1001 do not need to attempt this task, and cannot earn marks for it.

For this task, ensure your program works when GRID  SIZE is changed in constants. py to a di↵erent value. We will only test your code with GRID  SIZE values in the range [2, 8].  How- ever, you ideally should not hardcode your solution to only work for values in the range 2 to 8; these are simply provided as guarantees for the range of values we will test within.  A&