Last active
December 2, 2015 10:37
-
-
Save X140Yu/452e33486184d6914cd2 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
#!/usr/bin/python | |
# -*- coding: UTF-8 -*- | |
# 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())) | |
# end is_valid_int | |
def check_is_valid_file(file_name, board, width, height, ai_board): | |
""" | |
check if the file meets the need | |
@file_name: the file name that player input | |
@board: the player's board | |
@width: board's width | |
@height: board's height | |
@ai_board: AI's board | |
@returns: print messages list | |
""" | |
# open the file with default mode "r"(read) | |
# if the file does't exit or other errors occurs | |
# the program'll exit with 0 | |
try: | |
file_to_read = open(file_name) | |
except: | |
print("Open file error!") | |
print("Terminating game.") | |
sys.exit(0) | |
# list for symbols eg.'['P', 'A']' | |
symbols_for_ship = [] | |
# remember the info of the user's ship | |
# for drawing the AI's ships | |
placements_info = [] | |
# read the file line by line | |
for each_line in file_to_read: | |
# delete the ' ' and '\n' chars in this line | |
# prepare for later process | |
each_line = each_line.strip('\n') | |
# minus index number is not permitted | |
if '-' in each_line: | |
print("Error %s is placed outside of the board." % each_line[0]) | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
# now placement is an array like '['A', '1', '1', '2', '1']' | |
placement = list(each_line) | |
# clear the space | |
placement = list(filter(lambda x: x != " ", placement)) | |
placements_info.append(placement) | |
# get the symbol of the ship | |
symbol = placement[0] | |
if symbol in ["x", "X", "o", "O", "*"]: | |
print("You should not use the reserved symbols.") | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
# to check whether it has duplicate elements | |
if symbol in symbols_for_ship: | |
# symobl is in the list | |
# exit the process | |
print("Error symbol %s is already in use." % symbol) | |
# close the file | |
file_to_read.close() | |
print("Terminating game") | |
sys.exit(0) | |
else: | |
# symobl is not in the list, put it in the list | |
symbols_for_ship.append(symbol) | |
# check if user placed the ship diagonally | |
point_one_x = int(placement[1]) | |
point_one_y = int(placement[2]) | |
point_two_x = int(placement[3]) | |
point_two_y = int(placement[4]) | |
if point_one_x != point_two_x and point_one_y != point_two_y: | |
print("Ships cannot be placed diagonally.") | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
# point is (3, 0) but height is 3, the point is still out the board | |
# so this should be >= | |
if point_one_x >= height or point_two_x >= height or point_one_y >= width or point_two_y >= width: | |
print("Error %s is placed outside of the board." % each_line[0]) | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
# put the ship in the board to check whether place their ships on top of each other | |
if point_one_x == point_two_x: | |
# ship is horizontal | |
# guarantee point_one_y is less than point_two_y | |
if point_one_y > point_two_y: | |
# exchange the values | |
(point_one_y, point_two_y) = (point_two_y, point_one_y) | |
for y in range(point_one_y, point_two_y + 1): | |
if board[point_one_x][y] != "*": | |
# print("You place the ship on top of each other") | |
print("There is already a ship at location %d, %d." % (point_one_x, y)) | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
board[point_one_x][y] = symbol | |
else: | |
# ship is vertical | |
if point_one_x > point_two_x: | |
(point_one_x, point_two_x) = (point_two_x, point_one_x) | |
for x in range(point_one_x, point_two_x + 1): | |
if board[x][point_one_y] != "*": | |
# print("You place the ship on top of each other") | |
print("There is already a ship at location %d, %d." % (x, point_one_y)) | |
file_to_read.close() | |
print("Terminating game.") | |
sys.exit(0) | |
board[x][point_one_y] = symbol | |
# end of reading file | |
file_to_read.close() | |
# draw the AI's board at the same time | |
messages = draw_ai_board(placements_info, width, height, ai_board) | |
return messages | |
# end check_is_valid_file | |
def is_valid_ai_level(ai_level): | |
""" | |
check if the user input the right AI Level | |
@ai_level: the ai_level that user input | |
@returns: whether the level is valid | |
""" | |
if ai_level in ["1", "2", "3"]: | |
return True | |
else: | |
return False | |
# end is_valid_ai_level | |
def is_valid_user_guess_point(user_guess_string, ai_board): | |
""" | |
check if the user guess point is valid | |
@user_guess_string: user input, eg. '2 0' is valid | |
@ai_board: the AI's board to check | |
@returns: True or False | |
""" | |
str_guess = user_guess_string.split() | |
# tear it to two parts | |
if len(str_guess) != 2: | |
return False | |
# unwrap it | |
(row, col) = str_guess | |
if not row.isdigit() or not col.isdigit(): | |
return False | |
row = int(row) | |
col = int(col) | |
# whether the row or col is out of the board | |
try: | |
ai_board[row][col] | |
except: | |
return False | |
# fire on a right place? | |
if ai_board[row][col] == '*' or ai_board[row][col] != '.': | |
return True | |
return False | |
# end is_valid_user_guess_point | |
def get_guess_point(guess_string): | |
""" | |
get the point based on user input string(checked) | |
@guess_string: user's input string, eg. "0 1" | |
@returns: eg. (0, 1) | |
""" | |
(row, col) = guess_string.split() | |
row = int(row) | |
col = int(col) | |
return (row, col) | |
# end get_guess_point | |
def create_board(width, height): | |
""" | |
create the board | |
@width: the width of the board | |
@height: the height of the board | |
@returns: the created board | |
""" | |
board = [] | |
for row in range(height): | |
board.append(["*"] * width) | |
return board | |
# end create_board | |
def print_board(board, width, height): | |
""" | |
print the board with format | |
@board: the AI or player's board | |
@width: width of the board | |
@height: height of the board | |
""" | |
# print the first line | |
# ' 0 1 2 ..' | |
print(" ", end="") | |
row_index = 0 | |
for num in range(width): | |
print ((' %d' % row_index), end="") | |
row_index += 1 | |
print("") | |
#print the rest of the board | |
index = 0 | |
for row in board: | |
print(('%d ' % index) + " ".join(row)) | |
index += 1 | |
# end print_board | |
def print_all_boards_status(ai_board_to_explore, board, width, height): | |
""" | |
print all boards | |
@ai_board_to_explore: AI's showing board | |
@board: player's board | |
@width: board's width | |
@height: board's height | |
""" | |
# print the AI's board | |
print("Scanning Board") | |
print_board(ai_board_to_explore, width, height) | |
# print the user's board | |
print("") | |
print("My Board") | |
print_board(board, width, height) | |
print("") | |
# end print_all_boards_status | |
def get_ship_length(placement): | |
""" | |
get the ship length using placement | |
@placement: eg. '['A', '1', '1', '2', '1']' | |
@returns: the length of the ship | |
""" | |
point_one_x = int(placement[1]) | |
point_one_y = int(placement[2]) | |
point_two_x = int(placement[3]) | |
point_two_y = int(placement[4]) | |
if point_one_x == point_two_x: | |
return abs(point_one_y - point_two_y) + 1 | |
else: | |
return abs(point_one_x - point_two_x) + 1 | |
# end get_ship_length | |
def draw_ai_board(placements_info, width, height, ai_board): | |
""" | |
draw AI's board based on user's placements_info | |
@placements_info: user's placements info | |
@width: board's width | |
@height: board's height | |
@returns: print messages list | |
""" | |
# place the ships in order based on the ship's symbol | |
placements_info = sorted(placements_info, key=lambda x: x[0]) | |
messages = [] | |
for placement in placements_info: | |
direction = '' | |
row = -1 | |
col = -1 | |
ship_length = get_ship_length(placement) | |
symbol = placement[0] | |
while not can_draw_on_the_ai_board(ai_board, symbol, direction, row, col, ship_length, width, height): | |
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) | |
# direction, row, col is ready | |
message = draw_ship_on_the_ai_board(ai_board, symbol, direction, row, col, ship_length) | |
# print_board(ai_board, width, height) | |
messages.append(message) | |
return messages | |
# end draw_ai_board | |
def can_draw_on_the_ai_board(ai_board, symbol, direction, row, col, length, width, height): | |
""" | |
whether AI can draw on the board with | |
@symbol: symbol of the ship, eg. 'P' | |
@directon: direction of the ship eg.'horz' | |
@row: which row to draw the ship | |
@col: which col to draw the ship | |
@length: the length of the ship | |
@ai_board: the board of AI | |
@width: AI board's width | |
@height: AI board's height | |
@returns: True or False | |
""" | |
# copy the board, in case change the origin board | |
# ai_board is 2D list, so has to copy like this | |
temp_board = list(map(list, ai_board)) | |
if row < 0 and col < 0: | |
return False | |
if direction == 'horz': | |
for i in range(col, col + length): | |
if temp_board[row][i] != '*': | |
return False | |
else: | |
temp_board[row][i] = symbol | |
else: | |
for i in range(row, row + length): | |
if temp_board[i][col] != '*': | |
return False | |
else: | |
temp_board[i][col] = symbol | |
return True | |
# end can_draw_on_the_ai_board | |
def draw_ship_on_the_ai_board(ai_board, symbol, direction, row, col, length): | |
""" | |
draw ship on the AI's board with | |
@ai_board: the board of AI | |
@symbol: symbol of the ship | |
@directon: direction of the ship | |
@row: which row to draw the ship | |
@col: which col to draw the ship | |
@length: the length of the ship | |
@returns: print messages, eg. 'Placing ship from 0,0 to 0,1.' | |
""" | |
message = '' | |
if direction == 'horz': | |
for i in range(col, col + length): | |
ai_board[row][i] = symbol | |
message = "Placing ship from %d,%d to %d,%d." % (row, col, row, col + length - 1) | |
else: | |
for i in range(row, row + length): | |
ai_board[i][col] = symbol | |
message = "Placing ship from %d,%d to %d,%d." % (row, col, row + length - 1, col) | |
return message | |
# end draw_ship_on_the_ai_board | |
def write_change_to_ai_board(ai_board_to_explore, ai_board, row, col): | |
""" | |
write change to AI's board | |
@ai_board_to_explore: AI's showing board | |
@ai_board: AI's origin board | |
@row, col: the point's location | |
@return: whether user hit a target | |
""" | |
# get the point on the board | |
# maybe '*' or symbol of the ship | |
symbol = ai_board[row][col] | |
if symbol == '*': | |
# missed | |
ai_board_to_explore[row][col] = 'O' | |
print("Miss!") | |
return False | |
else: | |
ai_board_to_explore[row][col] = 'X' | |
# if user hit the point, change the origin board point to '.' | |
# in case user hit it again | |
# if the origin board is all '*' and '.', user wins | |
sunk_symbol = ai_board[row][col] | |
ai_board[row][col] = '.' | |
symbols = sum(ai_board, []) | |
if not sunk_symbol in symbols: | |
print("You sunk my %s" % sunk_symbol) | |
return True | |
print("Hit!") | |
return True | |
# def write_change_to_ai_board | |
def check_wins(ai_board, board, width, height, do_print = False): | |
""" | |
check who wins the game | |
@ai_board: the AI's board | |
@Board: the user's board | |
@width: board's width | |
@height: board's height | |
@do_print: print the winning message? default is False | |
@returns: if somebody wins | |
""" | |
# if ai_board is all '*' and '.', or all '.', the user wins | |
ai_symbols = sum(ai_board, []) | |
if set(ai_symbols) == set(['*', '.']) or set(ai_symbols) == set(['.']): | |
if do_print: | |
print("You win!") | |
return True | |
# AI wins | |
user_symbols = sum(board, []) | |
if set(user_symbols) == set(['*', 'X']) or set(user_symbols) == set(['X']) or set(user_symbols) == set(['*', 'X', 'O']): | |
if do_print: | |
print("The AI wins.") | |
return True | |
# no one wins | |
return False | |
# end check_wins | |
def generate_ai_random_list(width, height): | |
""" | |
create the random mode of AI's list | |
eg. [[0, 0], [0, 1], [1,1]...] | |
@width: width of the board | |
@height: height of the board | |
@returns: the list | |
""" | |
ret_list = [] | |
for row in range(height): | |
for col in range(width): | |
ret_list.append([row, col]) | |
return ret_list | |
# end generate_ai_random_list | |
def write_change_to_user_board(point, board): | |
""" | |
write the change to user's board | |
@point: the valid point | |
@board: the user's board | |
@returns: hit or not | |
""" | |
row = point[0] | |
col = point[1] | |
symbol = board[row][col] | |
if symbol == '*': | |
# missed | |
board[row][col] = 'O' | |
print("Miss!") | |
return False | |
else: | |
# hit | |
sunk_symbol = board[row][col] | |
board[row][col] = 'X' | |
symbols = sum(board, []) | |
if not sunk_symbol in symbols: | |
print("You sunk my %s" % sunk_symbol) | |
return True | |
print("Hit!") | |
return True | |
# end write_change_to_user_board | |
def add_points_to_destory_list(destory_list, point, board, width, height): | |
""" | |
add the point's around points into the destory list | |
@destory_list: the destory list | |
@point: the point | |
@board: player's board | |
@width: board's width | |
@height: board's height | |
""" | |
row = point[0] | |
col = point[1] | |
# above | |
new_row = row - 1 | |
new_col = col | |
if new_row >= 0 and board[new_row][new_col] != 'O' and 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 board[new_row][new_col] != 'O' and 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 board[new_row][new_col] != 'O' and 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 board[new_row][new_col] != 'O' and board[new_row][new_col] != 'X': | |
if not [new_row, new_col] in destory_list: | |
destory_list.append([new_row, new_col]) | |
# end add_points_to_destory_list | |
def cheating_ai_write_change_to_user_board(board, width, height): | |
""" | |
level 3 AI's move | |
@board: player's board | |
@width: board's width | |
@height: board's height | |
@returns: hit or not | |
""" | |
for x in range(height): | |
for y in range(width): | |
symbol = board[x][y] | |
if symbol != '*' and symbol != 'X': | |
print("The AI fires at location (%d, %d)" % (x, y)) | |
write_change_to_user_board([x, y], board) | |
return True | |
return False | |
# def cheating_ai_write_change_to_user_board | |
def play_battleship(): | |
""" | |
play the game of battleship | |
""" | |
# ask player to enter the seed | |
seed = '' | |
while not is_valid_int(seed): | |
seed = input("Enter the seed: ") | |
seed = int(seed) | |
# ask player to enter width and height | |
width = '' | |
while not width.isdigit(): | |
width = input("Enter the width of the board: ") | |
# width is a string, change it to int | |
width = int(width) | |
height = '' | |
while not height.isdigit(): | |
height = input("Enter the height of the board: ") | |
height = int(height) | |
# ask player to enter the file name | |
file_name = input("Enter the name of the file containing your ship placements: ") | |
# ask player to choose AI | |
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) | |
# print the AI's board | |
ai_board_to_explore = create_board(width, height) | |
# 1. Seed the random number generator with the provided seed | |
random.seed(seed) | |
# 2. Construct the user's board | |
board = create_board(width, height) | |
# 3. Construct the AI's board | |
ai_board = create_board(width, height) | |
# print placing ship messages | |
# eg. Placing ship from 2,0 to 3,0. | |
messages = check_is_valid_file(file_name, board, width, height, ai_board) | |
for msg in messages: | |
print(msg) | |
# who to first | |
who_first = random.randint(0, 1) | |
# list for level 1 AI | |
if ai_level == 1: | |
random_ai_list = generate_ai_random_list(width, height) | |
if ai_level == 2: | |
# list for hunt mode | |
random_ai_list = generate_ai_random_list(width, height) | |
destory_list = [] | |
if who_first == 0: | |
print_all_boards_status(ai_board_to_explore, board, width, height) | |
while not check_wins(ai_board, board, width, height, do_print=True): | |
if who_first == 0: | |
# player goes | |
user_guess_point = '' | |
while not is_valid_user_guess_point(user_guess_point, ai_board): | |
try: | |
user_guess_point = input("Enter row and column to fire on separated by a space: ") | |
except: | |
# EOFError | |
pass | |
# get the point(two ints) | |
(guess_row, guess_col) = get_guess_point(user_guess_point) | |
# write the change to the AI's board | |
write_change_to_ai_board(ai_board_to_explore, ai_board, guess_row, guess_col) | |
if check_wins(ai_board, board, width, height) == True: | |
print_all_boards_status(ai_board_to_explore, board, width, height) | |
who_first = 1 | |
else: | |
# AI goes | |
if ai_level == 1: | |
# random search | |
point = random.choice(random_ai_list) | |
random_ai_list.remove(point) | |
print("The AI fires at location (%d, %d)" % (point[0], point[1])) | |
# make changes to the board | |
write_change_to_user_board(point, board) | |
elif ai_level == 2: | |
# get a point in the hunter list | |
if len(destory_list) > 0: | |
# destory mode | |
point = destory_list[0] | |
else: | |
# hunt mode | |
point = random.choice(random_ai_list) | |
print("The AI fires at location (%d, %d)" % (point[0], point[1])) | |
# remove point in two lists | |
if point in random_ai_list: | |
random_ai_list.remove(point) | |
if point in destory_list: | |
destory_list.remove(point) | |
if write_change_to_user_board(point, board) == True: | |
# AI hits the ship, add points to destory_list | |
add_points_to_destory_list(destory_list, point, board, width, height) | |
else: | |
# ai_level == 3: | |
cheating_ai_write_change_to_user_board(board, width, height) | |
print_all_boards_status(ai_board_to_explore, board, width, height) | |
who_first = 0 | |
# end while | |
# end play_battleship | |
if __name__ == '__main__': | |
play_battleship() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment