Skip to content

Instantly share code, notes, and snippets.

@X140Yu
Last active December 2, 2015 10:37
Show Gist options
  • Save X140Yu/452e33486184d6914cd2 to your computer and use it in GitHub Desktop.
Save X140Yu/452e33486184d6914cd2 to your computer and use it in GitHub Desktop.
#!/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