Created
December 4, 2015 17:28
-
-
Save X140Yu/e1767db4677f32d6e4d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# play the battleship game | |
import sys | |
import random | |
def is_valid_int(integer): | |
""" | |
checks to see if number represents a valid integer | |
@number: a string that might represent an integer | |
@returns: true if the string represents an integer | |
""" | |
integer = integer.strip() | |
if len(integer) == 0: | |
return False | |
else: | |
return (integer.isdigit() or #only digits | |
#or a negative sign followed by digits | |
(integer.startswith('-') and integer[1:].isdigit())) | |
def create_board(width, height): | |
""" | |
create the play board | |
@width: width of the board which is int | |
@height: height of the board, int | |
@returns: list of list characters, represents the board | |
""" | |
board = [] | |
for board_row in range(height): | |
board.append(["*"] * width) | |
return board | |
def get_seed_input(): | |
""" | |
get user's seed input | |
@returns: seed number, should be an int | |
""" | |
# get the seed | |
seed = " " | |
while not is_valid_int(seed): | |
seed = input("Enter the seed: ") | |
# change it to int | |
seed = int(seed) | |
return seed | |
def get_width_and_height_input(): | |
""" | |
get user's width and height input | |
@returns: widht and height, both int | |
""" | |
# get the width | |
width = " " | |
while not width.isdigit(): | |
width = input("Enter the width of the board: ") | |
# change it to int | |
width = int(width) | |
# get the height | |
height = " " | |
while not height.isdigit(): | |
height = input("Enter the height of the board: ") | |
# change it to int | |
height = int(height) | |
return (width, height) | |
def is_valid_ai_level(ai_level): | |
""" | |
check the AI Level input | |
@ai_level: a character represents the AI's level | |
@returns: true if the level is right | |
""" | |
if ai_level == "1": | |
return True | |
elif ai_level == "2": | |
return True | |
elif ai_level == "3": | |
return True | |
else: | |
return False | |
def get_ai_level(): | |
""" | |
get user's AI levle input | |
@returns: AI's level, which is an int | |
""" | |
ai_level = "" | |
while not is_valid_ai_level(ai_level): | |
ai_level = input("Choose your AI.\n1. Random\n2. Smart\n3. Cheater\n Your choice: ") | |
ai_level = int(ai_level) | |
return ai_level | |
def check_minus_location(line): | |
""" | |
if user put the ship with minus point | |
@line: a line of strings | |
@returns: None | |
""" | |
# if number less than 0, there is a problem | |
for character in line: | |
if character == '-': | |
print("Error %s is placed outside of the board." % line[0]) | |
print("Terminating game.") | |
sys.exit(0) | |
def change_line_to_one_ship_list(line): | |
""" | |
change a line to a ship list | |
@line: a line of strings | |
@returns: a list, represents a ship location info | |
like ["P", "0", "1", "2", "3"] | |
""" | |
ship_location = list(line) | |
# remove the space and "\n" | |
ship_location = list(filter(lambda x: x != " " and x != "\n", ship_location)) | |
return ship_location | |
def check_reserved_symbols(name_of_the_ship): | |
""" | |
check if use the reserved symbols | |
@name_of_the_ship: symbol, a character | |
@returns: None | |
""" | |
# if use the reserved symbols | |
if name_of_the_ship in ["x", "X", "o", "O", "*"]: | |
print("You should not use the reserved symbols.") | |
print("Terminating game.") | |
sys.exit(0) | |
def check_file_content(board_file_name, width, height): | |
""" | |
check the user's input file is right or not | |
@board_file_name: file's name, string | |
@width: the board's width, int | |
@height: the board's height, int | |
@returns: the user ships's locations, should be a list | |
""" | |
# open the file | |
try: | |
board_file = open(board_file_name) | |
except: | |
# open file failed | |
print("File cannot open.") | |
print("Terminating game.") | |
sys.exit(0) | |
# list for symbols eg.'['P', 'A']' | |
# check duplicate | |
ship_names = [] | |
# a board to check if user put the ship at the same place | |
board = create_board(width, height) | |
# the user ships's locations | |
user_location_list = [] | |
# iterate the file with line | |
for line in board_file: | |
# if user put the ship with minus point | |
check_minus_location(line) | |
# change a line to a ship list | |
ship_location = change_line_to_one_ship_list(line) | |
# the name of the ship | |
name_of_the_ship = ship_location[0] | |
# the two points of the ship | |
first_point_x = int(ship_location[1]) | |
first_point_y = int(ship_location[2]) | |
second_point_x = int(ship_location[3]) | |
second_point_y = int(ship_location[4]) | |
# check if use the reserved symbols | |
check_reserved_symbols(name_of_the_ship) | |
# check outside the board | |
if first_point_x >= height or second_point_x >= height or first_point_y >= width or second_point_y >= width: | |
print("Error %s is placed outside of the board." % line[0]) | |
print("Terminating game.") | |
sys.exit(0) | |
# if has duplicate names | |
if name_of_the_ship in ship_names: | |
print("Error symbol %s is already in use." % name_of_the_ship) | |
print("Terminating game") | |
sys.exit(0) | |
# put the name in list | |
ship_names.append(name_of_the_ship) | |
# if user place diagonal ship | |
if first_point_x != second_point_x and first_point_y != second_point_y: | |
print("Ships cannot be placed diagonally.") | |
print("Terminating game.") | |
sys.exit(0) | |
user_location_list.append(ship_location) | |
# check if user put the ship on another ship | |
# horizontal | |
if first_point_x == second_point_x: | |
# first_point_y should less than second_point_y | |
if first_point_y > second_point_y: | |
temp = first_point_y | |
first_point_y = second_point_y | |
second_point_y = temp | |
for y in range(first_point_y, second_point_y + 1): | |
if board[first_point_x][y] != "*": | |
print("There is already a ship at location %d, %d." % (first_point_x, y)) | |
print("Terminating game.") | |
sys.exit(0) | |
board[first_point_x][y] = name_of_the_ship | |
# vertical | |
else: | |
if first_point_x > second_point_x: | |
temp = first_point_x | |
first_point_x = second_point_x | |
second_point_x = temp | |
for x in range(first_point_x, second_point_x + 1): | |
if board[x][first_point_y] != "*": | |
print("There is already a ship at location %d, %d." % (x, first_point_y)) | |
print("Terminating game.") | |
sys.exit(0) | |
board[x][first_point_y] = name_of_the_ship | |
return user_location_list | |
def draw_user_board(user_location_list, user_board): | |
""" | |
draw user's board | |
@user_location_list: a list with user's location | |
@user_board: the user's board | |
@returns: None | |
""" | |
for ship_location in user_location_list: | |
# the name of the ship | |
name_of_the_ship = ship_location[0] | |
# the two points of the ship | |
first_point_x = int(ship_location[1]) | |
first_point_y = int(ship_location[2]) | |
second_point_x = int(ship_location[3]) | |
second_point_y = int(ship_location[4]) | |
# horizontal | |
if first_point_x == second_point_x: | |
# first_point_y should less than second_point_y | |
if first_point_y > second_point_y: | |
temp = first_point_y | |
first_point_y = second_point_y | |
second_point_y = temp | |
for y in range(first_point_y, second_point_y + 1): | |
user_board[first_point_x][y] = name_of_the_ship | |
# vertical | |
else: | |
if first_point_x > second_point_x: | |
temp = first_point_x | |
first_point_x = second_point_x | |
second_point_x = temp | |
for x in range(first_point_x, second_point_x + 1): | |
user_board[x][first_point_y] = name_of_the_ship | |
def get_ship_length(ship_location): | |
""" | |
get the ship length with ship_location | |
@ship_location: a list | |
@returns: ship's length, int | |
""" | |
# the name of the ship | |
name_of_the_ship = ship_location[0] | |
# the two points of the ship | |
first_point_x = int(ship_location[1]) | |
first_point_y = int(ship_location[2]) | |
second_point_x = int(ship_location[3]) | |
second_point_y = int(ship_location[4]) | |
length = 0 | |
if first_point_x == second_point_x: | |
length = abs(first_point_y - second_point_y) | |
else: | |
length = abs(first_point_x - second_point_x) | |
length += 1 | |
return length | |
def can_draw_on_the_ai_board(ai_board, direction, row, col, ship_length): | |
""" | |
check if point can draw on the ai_board | |
@ai_board: board of the AI | |
@direction: direction of the ship, 'horz' or 'vert' | |
@row: int, row number | |
@col: int, col number | |
@ship_length: int, length of the ship | |
""" | |
# ai_board is a list of list, copy it not changing it | |
ai_temp_board = [x[:] for x in ai_board] | |
# the first time | |
if row == " ": | |
return False | |
if direction == 'horz': | |
for i in range(col, col + ship_length): | |
if ai_temp_board[row][i] != '*': | |
return False | |
else: | |
ai_temp_board[row][i] = "-" | |
else: | |
for i in range(row, row + ship_length): | |
if ai_temp_board[i][col] != '*': | |
return False | |
else: | |
ai_temp_board[i][col] = "-" | |
return True | |
def draw_ai_real_board(user_location_list, ai_board): | |
""" | |
draw AI's board | |
@user_location_list: a list with user's location | |
@ai_board: the AI's board | |
@returns: None | |
""" | |
width = len(ai_board[0]) | |
height = len(ai_board) | |
# names should in order | |
user_location_list = sorted(user_location_list, key = lambda x: x[0]) | |
messages = [] | |
for ship_location in user_location_list: | |
direction = '' | |
row = " " | |
col = " " | |
ship_length = get_ship_length(ship_location) | |
ship_name = ship_location[0] | |
while not can_draw_on_the_ai_board(ai_board, direction, row, col, ship_length): | |
direction = random.choice(['vert', 'horz']) | |
if direction == 'horz': | |
row = random.randint(0, height - 1) | |
col = random.randint(0, width - ship_length) | |
else: | |
row = random.randint(0, height - ship_length) | |
col = random.randint(0, width - 1) | |
if direction == 'horz': | |
for i in range(col, col + ship_length): | |
ai_board[row][i] = ship_name | |
print("Placing ship from %d,%d to %d,%d." % (row, col, row, col + ship_length - 1)) | |
else: | |
for i in range(row, row + ship_length): | |
ai_board[i][col] = ship_name | |
print("Placing ship from %d,%d to %d,%d." % (row, col, row + ship_length - 1, col)) | |
def print_board(board): | |
""" | |
print the board | |
@board: list of list characters | |
@returns: None | |
""" | |
width = len(board[0]) | |
height = len(board) | |
# to fit the test | |
print(" ", end="") | |
row_index = 0 | |
for num in range(width): | |
print ((' %d' % row_index), end="") | |
row_index += 1 | |
print("") | |
col_index = 0 | |
for row in board: | |
print(('%d ' % col_index) + " ".join(row)) | |
col_index += 1 | |
def print_both_boards(ai_board_blank, user_board): | |
""" | |
print both the boards | |
@ai_board_blank: the ai board showing to user | |
@user_board: user's board | |
both list of list | |
""" | |
width = len(ai_board_blank[0]) | |
height = len(ai_board_blank) | |
# print AI's board | |
print("Scanning Board") | |
print_board(ai_board_blank) | |
# print user's board | |
print("My Board") | |
print_board(user_board) | |
def check_wins_with_print(ai_board, user_board): | |
""" | |
check who wins with print info | |
@ai_board: AI's board | |
@user_board: user's board | |
@returns: true if someone wins | |
""" | |
# user wins | |
# change ai_board to a list | |
ai_sums = sum(ai_board, []) | |
if set(ai_sums) == set(['*', 'X']) or set(ai_sums) == set(['X']): | |
print("You win!") | |
return True | |
# AI wins | |
user_sums = sum(user_board, []) | |
if set(user_sums) == set(['X']) or set(user_sums) == set(['*', 'X']) or set(user_sums) == set(['*', 'X', 'O']): | |
print("The AI wins.") | |
return True | |
return False | |
def check_wins_without_print(ai_board, user_board): | |
""" | |
check who wins without print info | |
@ai_board: AI's board | |
@user_board: user's board | |
@returns: true if someone wins | |
""" | |
# user wins | |
# change ai_board to a list | |
ai_sums = sum(ai_board, []) | |
if set(ai_sums) == set(['*', 'X']) or set(ai_sums) == set(['X']): | |
return True | |
# AI wins | |
user_sums = sum(user_board, []) | |
if set(user_sums) == set(['X']) or set(user_sums) == set(['*', 'X']) or set(user_sums) == set(['*', 'X', 'O']): | |
return True | |
return False | |
def is_valid_user_guess_point(user_guess_string, ai_board): | |
""" | |
check if the user guess point is right | |
@user_guess_string: user's guess string | |
@ai_board: AI's board | |
@returns: Ture if the string is right | |
""" | |
width = len(ai_board[0]) | |
height = len(ai_board) | |
guess = user_guess_string.split() | |
if len(guess) != 2: | |
return False | |
# unwrap | |
(row, col) = guess | |
if not row.isdigit() or not col.isdigit(): | |
return False | |
# change to int | |
row = int(row) | |
col = int(col) | |
# out the board | |
if col >= width or row >= height: | |
return False | |
if ai_board[row][col] == '*' or ai_board[row][col] != '.': | |
return True | |
return False | |
def get_user_guess(ai_board): | |
""" | |
get user's input | |
@returns: row and col both int | |
""" | |
user_guess_point = '' | |
while not is_valid_user_guess_point(user_guess_point, ai_board): | |
user_guess_point = input("Enter row and column to fire on separated by a space: ") | |
(row, col) = user_guess_point.split() | |
row = int(row) | |
col = int(col) | |
return (row, col) | |
def draw_point_on_ai_board(guess_point, ai_board_blank, ai_board): | |
""" | |
put the point on the ai_board_blank | |
@guess_point: the guess point, (int, int) | |
@ai_board_blank: the blank AI board | |
@ai_board: AI's real board | |
@returns: None | |
""" | |
(row, col) = guess_point | |
symbol = ai_board[row][col] | |
# missed | |
if symbol == '*': | |
ai_board_blank[row][col] = 'O' | |
print("Miss!") | |
else: # hit! | |
ai_board_blank[row][col] = 'X' | |
sunk_name = ai_board[row][col] | |
ai_board[row][col] = 'X' | |
symbols = sum(ai_board, []) | |
if not sunk_name in symbols: | |
print("You sunk my %s" % sunk_name) | |
else: | |
print("Hit!") | |
def draw_point_on_user_board(point, user_board): | |
""" | |
put the point on the user's board | |
@point: the guess point | |
@user_board: the user's board | |
@returns: true if the AI hit a ship | |
""" | |
row = point[0] | |
col = point[1] | |
symbol = user_board[row][col] | |
if symbol == '*': | |
# missed | |
user_board[row][col] = 'O' | |
print("Miss!") | |
return False | |
else: | |
# hit | |
sunk_symbol = user_board[row][col] | |
user_board[row][col] = 'X' | |
symbols = sum(user_board, []) | |
if not sunk_symbol in symbols: | |
print("You sunk my %s" % sunk_symbol) | |
return True | |
else: | |
print("Hit!") | |
return True | |
def create_ai_random_list(width, height): | |
""" | |
create AI's random list | |
@width: width of the board | |
@height: height of the board | |
@returns: the created list | |
""" | |
random_list = [] | |
for row in range(height): | |
for col in range(width): | |
random_list.append([row, col]) | |
return random_list | |
def level_one_ai_turn(random_ai_list, user_board): | |
""" | |
level one AI plays the game | |
@random_ai_list: the rest of the random list | |
@user_board: user's board | |
@returns: None | |
""" | |
ai_guess_point = random.choice(random_ai_list) | |
random_ai_list.remove(ai_guess_point) | |
print("The AI fires at location (%d, %d)" % (ai_guess_point[0], ai_guess_point[1])) | |
draw_point_on_user_board(ai_guess_point, user_board) | |
def level_two_ai_turn(random_ai_list, destory_list, user_board): | |
""" | |
level Two AI plays the game | |
@random_ai_list: the rest of the random list | |
@destory_list: the destory list | |
@user_board: user's board | |
@returns: None | |
""" | |
if len(destory_list) > 0: | |
# destory mode | |
ai_guess_point = destory_list[0] | |
else: | |
# hunt mode | |
ai_guess_point = random.choice(random_ai_list) | |
if ai_guess_point in destory_list: | |
destory_list.remove(ai_guess_point) | |
if ai_guess_point in random_ai_list: | |
random_ai_list.remove(ai_guess_point) | |
print("The AI fires at location (%d, %d)" % (ai_guess_point[0], ai_guess_point[1])) | |
if draw_point_on_user_board(ai_guess_point, user_board) == True: | |
#add point to destory_list | |
width = len(user_board[0]) | |
height = len(user_board) | |
row = ai_guess_point[0] | |
col = ai_guess_point[1] | |
# up | |
new_row = row - 1 | |
new_col = col | |
if new_row >= 0 and user_board[new_row][new_col] != 'O' and user_board[new_row][new_col] != 'X': | |
if not [new_row, new_col] in destory_list: | |
destory_list.append([new_row, new_col]) | |
# below | |
new_row = row + 1 | |
new_col = col | |
if new_row < height and user_board[new_row][new_col] != 'O' and user_board[new_row][new_col] != 'X': | |
if not [new_row, new_col] in destory_list: | |
destory_list.append([new_row, new_col]) | |
# left | |
new_row = row | |
new_col = col - 1 | |
if new_col >= 0 and user_board[new_row][new_col] != 'O' and user_board[new_row][new_col] != 'X': | |
if not [new_row, new_col] in destory_list: | |
destory_list.append([new_row, new_col]) | |
# right | |
new_row = row | |
new_col = col + 1 | |
if new_col < width and user_board[new_row][new_col] != 'O' and user_board[new_row][new_col] != 'X': | |
if not [new_row, new_col] in destory_list: | |
destory_list.append([new_row, new_col]) | |
def level_three_ai_turn(user_board): | |
""" | |
level three AI plays the game | |
@user_board: user's board | |
@returns: None | |
""" | |
width = len(user_board[0]) | |
height = len(user_board) | |
for x in range(height): | |
for y in range(width): | |
symbol = user_board[x][y] | |
if symbol != '*' and symbol != 'X': | |
print("The AI fires at location (%d, %d)" % (x, y)) | |
draw_point_on_user_board([x, y], user_board) | |
return | |
def play_game(): | |
""" | |
play the battleship game | |
""" | |
# -------- get user's input | |
# get the seed | |
seed = get_seed_input() | |
# get the width and height | |
(width, height) = get_width_and_height_input() | |
# get the file name | |
board_file_name = input("Enter the name of the file containing your ship placements: ") | |
# get the AI level | |
ai_level = get_ai_level() | |
# -------- game's setup and check the file contnet | |
# Seed the random number generator with the provided seed | |
random.seed(seed) | |
# create the blank AI board | |
ai_board_blank = create_board(width, height) | |
# Construct the user's board | |
user_board = create_board(width, height) | |
# Construct the AI's board | |
ai_board = create_board(width, height) | |
# check if the file content is right | |
user_location_list = check_file_content(board_file_name, width, height) | |
# --------- file's checked, draw the board of user | |
# draw the user's board | |
draw_user_board(user_location_list, user_board) | |
# draw the AI's real board | |
draw_ai_real_board(user_location_list, ai_board) | |
# --------- random the seed to decide who is the first | |
who_is_the_first = random.randint(0, 1) | |
# if user's the first, should print the boards info first to fit the test | |
if who_is_the_first == 0: | |
print_both_boards(ai_board_blank, user_board) | |
# Construct a random list for AI level 1 and 2 | |
random_ai_list = create_ai_random_list(width, height) | |
# Construct the destory list for AI level 2 destory mode | |
destory_list = [] | |
while not check_wins_with_print(ai_board, user_board): | |
# ------- user's turn | |
if who_is_the_first == 0: | |
# get the user's guess point | |
guess_point = get_user_guess(ai_board) | |
# change AI's board | |
draw_point_on_ai_board(guess_point, ai_board_blank, ai_board) | |
# print if needed to fit the test | |
if check_wins_without_print(ai_board, user_board) == True: | |
print_both_boards(ai_board_blank, user_board) | |
who_is_the_first = 1 | |
else: | |
# ------- AI's turn | |
if ai_level == 1: | |
level_one_ai_turn(random_ai_list, user_board) | |
elif ai_level == 2: | |
level_two_ai_turn(random_ai_list, destory_list, user_board) | |
else: | |
level_three_ai_turn(user_board) | |
print_both_boards(ai_board_blank, user_board) | |
who_is_the_first = 0 | |
if __name__ == '__main__': | |
play_game() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment