Last active
August 29, 2015 14:08
-
-
Save whudgins/afe1c44a3cb0b6af00b5 to your computer and use it in GitHub Desktop.
Tic Tac Toe
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
######################################### | |
# A simple Tic-Tac-Toe game in Ruby | |
# Author: Will Hudgins | |
######################################### | |
#### Todos: | |
# => Abstract out methods, refactor, clean up | |
# => Clean up AI code: Definitely a more beautiful way to elaborate the strategies | |
# => Implement something based on this: http://www.neverstopbuilding.com/minimax | |
@board_position = {"1" => [0,0], "2" => [0,1], "3" => [0,2], "4" => [1,0], "5" => [1,1], "6" => [1,2], "7" => [2,0], "8" => [2,1], "9" => [2,2]} | |
@move_message = ["Good move.", "Well played, sir or madam.", "Bold strategy Cotton, we'll see how that one plays out for them.", "Bravo.", "Are you sure about that?", "Not what I would have done...", "Well that's certainly one way of going about it."] | |
@lose_message = ["Baha! You lost to a machine.", "Better luck next time, I guess.", "Is that how it feels to lose? I wouldn't know.", "Amateur.", "Try again... if you dare."] | |
@win_message = ["What! I never lose. Must be a bug...", "You win. This must be a mistake...", "Game, set, match. Well done", "Well then... Hail to my human overlord.", "Enjoy your victory, it'll be your last."] | |
@ai_play_message = ["Here goes nothing.", "Prepare for domination.", "Feast your eyes upon this beauty.", "Well then, here's my move."] | |
def start_game | |
board = [[" "," "," "],[" "," "," "],[" "," "," "]] | |
print_board(board) | |
puts "Welcome to Tic-Tac-Toe!" | |
return board | |
end | |
def play_game(board) | |
if check_winning_board(board) | |
puts @lose_message.sample | |
return | |
end | |
if !check_winning_board(board) and !board_full(board) | |
print "Make a move by entering a position on the board 1-9: " | |
move = gets.chomp | |
board = do_turn(move.to_i, board) | |
if board_full(board) | |
return | |
end | |
board = make_AI_move(board, move.to_i) | |
print_board(board) | |
puts @move_message.sample | |
puts @ai_play_message.sample | |
play_game(board) | |
end | |
end | |
def do_turn(move, board) | |
# error catching | |
if move < 1 or move > 9 | |
puts "You've made an invalid move. Try again!" | |
play_game(board) | |
end | |
x_spots = get_spots(board, "X") | |
o_spots = get_spots(board, "O") | |
if x_spots.include?(move.to_s) or o_spots.include?(move.to_s) | |
puts "You've made an invalid move. Try again!" | |
play_game(board) | |
end | |
position = get_board_position(move) | |
board = place_move(position, board, "X") | |
if check_winning_board(board) | |
puts @win_message.sample | |
end | |
if board_full(board) | |
print_board(board) | |
puts "Game over. Until we meet again." | |
end | |
return board | |
end | |
def make_AI_move(board, move) | |
puts | |
puts @ai_play_message.sample | |
ai_move = get_optimal_move(board) | |
position = get_board_position(ai_move) | |
board = place_move(position, board, "O") | |
return board | |
end | |
def get_spots(board, char) | |
spots = [] | |
i = 0 | |
while i < 3 | |
j = 0 | |
while j < 3 | |
if board[i][j] == char.to_s | |
spots.push(@board_position.key([i,j])) | |
end | |
j += 1 | |
end | |
i += 1 | |
end | |
return spots | |
end | |
def place_move(position, board, char) | |
x = position[0] | |
y = position[1] | |
board[x][y] = char.to_s | |
return board | |
end | |
def get_board_position(move) | |
position = @board_position[move.to_s] | |
return position | |
end | |
def print_board(board) | |
system "clear" | |
print board[0] | |
puts | |
print board[1] | |
puts | |
print board[2] | |
puts | |
puts | |
end | |
def blank_board(board) | |
empty_spots = get_spots(board, " ") | |
return empty_spots == ["1", "2", "3", "4", "5", "6", "7", "8", "9"] | |
end | |
def board_full(board) | |
empty_spots = get_spots(board, " ") | |
if empty_spots.any? | |
return false | |
end | |
return true | |
end | |
def check_winning_board(board) | |
if !blank_board(board) | |
if check_horizontal_win(board) | |
return true | |
elsif check_vertical_win(board) | |
return true | |
elsif check_diagonal_win(board) | |
return true | |
end | |
end | |
return false | |
end | |
def check_diagonal_win(board) | |
x_spots = get_spots(board, "X") | |
o_spots = get_spots(board, "O") | |
if x_spots.include?("1") and x_spots.include?("5") and x_spots.include?("9") | |
return true | |
elsif x_spots.include?("3") and x_spots.include?("5") and x_spots.include?("7") | |
return true | |
end | |
if o_spots.include?("1") and o_spots.include?("5") and o_spots.include?("9") | |
return true | |
elsif o_spots.include?("3") and o_spots.include?("5") and o_spots.include?("7") | |
return true | |
end | |
return false | |
end | |
def check_horizontal_win(board) | |
i = 0 | |
while i < 3 | |
if board[i][0] != " " and board[i][0] == board[i][1] and board[i][1] == board[i][2] | |
return true | |
end | |
i += 1 | |
end | |
return false | |
end | |
def check_vertical_win(board) | |
i = 0 | |
while i < 3 | |
if board[0][i] != " " and board[0][i] == board[1][i] and board[1][i] == board[2][i] | |
return true | |
end | |
i += 1 | |
end | |
return false | |
end | |
def get_optimal_move(board) | |
empty_spots = get_spots(board, " ") | |
x_spots = get_spots(board, "X") | |
o_spots = get_spots(board, "O") | |
defensive_move = search_move(x_spots, empty_spots) | |
if defensive_move != nil | |
return defensive_move | |
end | |
offensive_move = search_move(o_spots, empty_spots) | |
if offensive_move != nil | |
return offensive_move | |
end | |
# best move in the game. if its open, take it! | |
if empty_spots.include?("5") | |
return "5" | |
# none met. pick a random empty spot. | |
else | |
ai_move = empty_spots.sample | |
return ai_move | |
end | |
end | |
def search_move(spots, empty_spots) | |
# clean this up! | |
# all the horizontal cases. | |
if spots.include?("1") and spots.include?("2") and empty_spots.include?("3") | |
return "3" | |
elsif spots.include?("1") and spots.include?("3") and empty_spots.include?("2") | |
return "2" | |
elsif spots.include?("2") and spots.include?("3") and empty_spots.include?("1") | |
return "1" | |
elsif spots.include?("4") and spots.include?("5") and empty_spots.include?("6") | |
return "6" | |
elsif spots.include?("4") and spots.include?("6") and empty_spots.include?("5") | |
return "5" | |
elsif spots.include?("5") and spots.include?("6") and empty_spots.include?("4") | |
return "4" | |
elsif spots.include?("7") and spots.include?("8") and empty_spots.include?("9") | |
return "9" | |
elsif spots.include?("7") and spots.include?("9") and empty_spots.include?("8") | |
return "8" | |
elsif spots.include?("8") and spots.include?("9") and empty_spots.include?("7") | |
return "7" | |
# vertical cases | |
elsif spots.include?("1") and spots.include?("4") and empty_spots.include?("7") | |
return "7" | |
elsif spots.include?("1") and spots.include?("7") and empty_spots.include?("4") | |
return "4" | |
elsif spots.include?("4") and spots.include?("7") and empty_spots.include?("1") | |
return "1" | |
elsif spots.include?("2") and spots.include?("5") and empty_spots.include?("8") | |
return "8" | |
elsif spots.include?("2") and spots.include?("8") and empty_spots.include?("5") | |
return "5" | |
elsif spots.include?("5") and spots.include?("8") and empty_spots.include?("2") | |
return "2" | |
elsif spots.include?("3") and spots.include?("6") and empty_spots.include?("9") | |
return "9" | |
elsif spots.include?("3") and spots.include?("9") and empty_spots.include?("6") | |
return "6" | |
elsif spots.include?("6") and spots.include?("9") and empty_spots.include?("3") | |
return "3" | |
# diagonal cases | |
elsif spots.include?("1") and spots.include?("5") and empty_spots.include?("9") | |
return "9" | |
elsif spots.include?("9") and spots.include?("5") and empty_spots.include?("1") | |
return "1" | |
elsif spots.include?("3") and spots.include?("5") and empty_spots.include?("7") | |
return "7" | |
elsif spots.include?("7") and spots.include?("5") and empty_spots.include?("3") | |
return "3" | |
end | |
end | |
play_game(start_game) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment