Created
September 18, 2017 04:07
-
-
Save spreered/2ca11608e2ccd139eea2db545d01a303 to your computer and use it in GitHub Desktop.
Tic Tac Toe created by FredHung - https://repl.it/LNmu/0
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
class Game | |
attr_accessor :state ,:player | |
# attr_accessor 會自動設定getter和setter | |
def initialize(k=[' ']*9 ,p='X') | |
@state = k | |
@player = p | |
end | |
def opponent | |
if @player == 'X' | |
return 'O' | |
else | |
return 'X' | |
end | |
end | |
def switchturn | |
if(@player == 'X') | |
@player ='O' | |
else | |
@player = 'X' | |
end | |
end | |
def setMove(move) | |
@state[move] = @player | |
# puts "::#{self} : setMove , state object_id is : #{@state.object_id}" | |
end | |
# print the state of game | |
def print_game | |
if is_new_game? | |
print(" \n") | |
puts(" 0 | 1 | 2 ") | |
puts("-----------") | |
puts(" 3 | 4 | 5 ") | |
puts("-----------") | |
puts(" 6 | 7 | 8 ") | |
print(" \n") | |
else | |
# print "\n" | |
# puts(" #{@state[0]} | #{@state[1]} | #{@state[2]} ") | |
# puts("-----------") | |
# puts(" #{@state[3]} | #{@state[4]} | #{@state[5]} ") | |
# puts("-----------") | |
# puts(" #{@state[6]} | #{@state[7]} | #{@state[8]} ") | |
# print "\n" | |
# print("\n") | |
@state.each_with_index do |value,index| | |
if value == ' ' | |
print " #{index} " | |
else | |
print " #{value} " | |
end | |
case index | |
when 2,5 | |
print("\n-----------\n") | |
when 8 | |
print("\n\n") | |
else | |
print("|") | |
end | |
end | |
end | |
end | |
#==game win? | |
def win?(player) | |
#player is X or O | |
if @state[0]==player | |
return true if (@state[3]==player && @state[6]==player ) || (@state[1]==player && @state[2]==player ) | |
end | |
if @state[4]==player | |
return true if (@state[0]==player && @state[8]==player ) || (@state[2]==player && @state[6]==player ) || (@state[1]==player && @state[7]==player ) || (@state[3]==player && @state[5]==player ) | |
end | |
if @state[8]==player | |
return true if (@state[2]==player && @state[5]==player ) || (@state[6]==player && @state[7]==player ) | |
end | |
return false | |
end | |
#==game over? | |
def over? | |
return true if(win?('X')||win?('O')||!@state.include?(' ')) | |
end | |
#== get_new_state :return new state | |
def clone_new_state(move) | |
new_game = self.clone | |
new_game.state= Array.new | |
@state.each do|i| | |
new_game.state<<i | |
end | |
new_game.setMove(move) | |
new_game.player = @player | |
new_game.switchturn | |
return new_game | |
end | |
#== get_available_moves :retrun available move [] | |
def get_available_moves | |
arr = Array.new(0) | |
(0...9).each do|i| | |
if @state[i] == ' ' | |
arr<<i | |
end | |
end | |
arr | |
end | |
#== is new game? ====== | |
def is_new_game? | |
@state.each do|i| | |
if i != ' ' | |
return false | |
end | |
end | |
return true | |
end | |
end | |
#=====class Game end==== | |
#===::getMove : get move form user=== | |
def getMove(game) | |
move = -1 | |
while move<0 || move > 8 | |
puts "Player #{game.player} : please enter your move between 0 to 8" | |
move = gets.chomp.to_i | |
if game.state[move] != ' ' | |
puts "this move is unavailable" | |
move = -1 | |
end | |
end | |
move | |
end | |
#====::getMove end========= | |
#===::score retrun the score in minimax==== | |
def score(game,depth,active_player) | |
#if game win? return 10-depth | |
#else if game lose return -10+depth | |
#else return 0 | |
# puts("::score : active_player:#{active_player}") | |
if game.win?(active_player) | |
return 10 - depth | |
elsif game.win?(game.opponent) | |
return depth -10 | |
else | |
return 0 | |
end | |
end | |
#====::score end========= | |
#====::minimax: return the minimax score==== | |
def minimax(game, depth,active_player) | |
choice = {} | |
# return hash choice={:move => best_move_value , :score => best_score } | |
scores = [] | |
#scores list of available moves | |
moves = [] | |
#available moves list | |
if game.over? | |
choice[:score]= score(game,depth,active_player) | |
# puts("retrun score") | |
return choice | |
end | |
depth = depth + 1 | |
# puts("::minimax: depth #{depth}, game.plyer #{game.player}, active_playe #{active_player}") | |
## collecttion the available moves | |
# puts("minimax: depth : #{depth} , get_available_moves : #{game.get_available_moves} ") | |
game.get_available_moves.each do|move| | |
try_game= game.clone_new_state(move) | |
# puts("::minimax : try game :depth : #{depth} ,game.plyer #{game.player}, try_game.player:#{try_game.player}try move : #{move} ") | |
# set move and change to next player | |
c = minimax(try_game,depth,active_player) | |
# puts("get score : #{c[:score]}, move: #{move}, try_game.player: #{try_game.player}") | |
scores<< c[:score] | |
moves<<move | |
# puts("::minimax : try game ") | |
# try_game.print | |
end | |
# print("moves : #{moves}, scores : #{scores} \n") | |
# print("---------end availabe_moves-----------\n") | |
#minimax calculation | |
if game.player == active_player | |
# return max {:move ,:score} | |
max_score_index = scores.each_with_index.max[1] | |
choice[:move] = moves[max_score_index] | |
choice[:score] = scores[max_score_index] | |
else #the opponent | |
#return min {:move ,:score} | |
min_score_index = scores.each_with_index.min[1] | |
choice[:move] = moves[min_score_index] | |
choice[:score] = scores[min_score_index] | |
end | |
# print("minimax return choice : #{choice}\n") | |
# game.print | |
# print("---------minimax-----------------\n") | |
return choice | |
end | |
#====::minimax end==== | |
#====::autoMove==== | |
def autoMove(game) | |
#ruten the best move by computer | |
# minimax return an hash[:move=>move, :score=>score] | |
# puts("::automove start!!!!!") | |
if game.is_new_game? | |
return rand(0...8) | |
end | |
choice = Hash.new | |
choice = minimax(game,0,game.player) | |
# puts("autoMove : #{choice}") | |
# puts("inside game") | |
# game.print | |
return choice[:move] | |
end | |
#==autoMove end==== | |
##===game start==== | |
game = Game.new([' ']*9,'X') | |
move = -1 | |
puts "Let's play a Game !" | |
userPick=' ' | |
until (userPick == 'X') || (userPick =='O') | |
puts("Please select the player you whant : X ? O ?") | |
userPick=gets.chomp | |
end | |
game.print_game | |
while !game.over? | |
puts("this is #{game.player}'s turn ") | |
if userPick == 'X' | |
if game.player == 'X' | |
move = getMove(game) | |
else | |
move = autoMove(game) | |
end | |
else | |
if game.player == 'O' | |
move = autoMove(game) | |
else | |
move = getMove(game) | |
end | |
end | |
game.setMove(move) | |
game.switchturn | |
game.print_game | |
end | |
puts "player #{game.player} take turn" | |
if(game.win?(game.player)) | |
puts "Player #{game.player} wins!!" | |
else | |
puts "end in a draw!" | |
end | |
=begin | |
建立一個棋局Game (class) | |
棋局狀態state | |
現在的玩家 player | |
method 換player | |
method 印出棋局 | |
method 輸入棋步 | |
method 判斷是否遊戲結束 | |
如果 win?(X) 或 win?(O) 或 沒格子 則遊戲結束 | |
method win? | |
如果player 三點連成一條線,則win | |
end Game | |
minimax | |
如果遊戲結束,回傳這格的分數,如果是player(電腦)則+10分 對手-10分 平手0分, | |
另外深度越深(步數),則得分越低(max= 10- depth mini= -10+depth) | |
如果還沒結束,尋訪每個可以走的棋步move | |
使用minimax算出這格的分數 | |
結束本迴圈,所有的棋步move可以算出相對的分數陣列 | |
挑選max分數 | |
如果現在的minimax假設棋局的player是電腦,回傳最高的分數和move | |
挑選mini分數 | |
如果現在minimax假設棋局的player是對手,回傳最低的分數和move | |
end minimax | |
電腦下棋 | |
如果是第一步棋,勝率相當,隨便挑一個 | |
其他的 使用minimax算出最佳棋步 | |
end 電腦下棋 | |
開始遊戲,玩家選擇X 或O ,X先攻 | |
while 遊戲結束(全部下完或者是有任一方贏) | |
X: 如果是使用者,請使用者輸入 | |
如果是電腦 請電腦下棋 | |
O: 如果是使用者,請使用者輸入 | |
如果是電腦 請電腦下棋 | |
印出現在的棋局 | |
end while | |
結束遊戲 輸出結果 | |
=end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment