From: Hans Fugal Date: 2004-12-12T04:12:28+09:00 Subject: Re: [QUIZ] Learning Tic-Tac-Toe (#11) --------------000008000504010303080605 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit It would be good to be able to play against eachother when this is all over, so I propose the following 'protocol'. It's not complicated and probably not ready for tournament tic-tac-toe ;-), but it should do nicely. 3 entities - a game server and two players. The game waits for players to connect. Upon connection the game sends an arbitrary hello line, then the character representing this player, and the board size: Ruby Quiz Tic-Tac-Toe server version 1.0 X 3x3 And the player responds with an arbitrary hello: hello my name is Juan When two players have joined, the game sends the current state to both players: ___ ___ ___ Then the game sends a message to the current player: move The player responds with an action: 1,1 The game then sends the state, or "move" again if it was an invalid move: ___ _X_ ___ game to player 2: move Player 2: 0,2 game to both: __O _X_ ___ ad nauseum. When the game is over, the game sends to the winner and loser, respectively: win lose The game then disconnects. White space is ignored, except for newlines (each message is on one line). The game state is in row-major order. Attached is such a server, and you are welcome to play a running version at fugal.net:1276. If you notice bugs please let me know - it has been through some rudimentary testing but not exhaustive testing. --------------000008000504010303080605 Content-Type: text/plain; name="server.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="server.rb" require 'socket' class TicTacToeServer < TCPServer def initialize(port,rows,cols) super(port) @cols = cols @rows = rows @z = 3 # 3 in a row reset end def reset @x = nil @o = nil @board = [] @rows.times { @board << '_'*@cols } end def state @board.join " " end def winner def check(s) return @x if s.include? "X"*@z return @o if s.include? "O"*@z false end @rows.times do |x| # check on the row ret = check(@board[x]) and return ret @cols.times do |y| # check on the column if x == 0 s = @board.collect {|row| row[y]}.join "" ret = check(s) and return ret end # check diagonally down and to the right if @cols - y >= @z and @rows - x >= @z s = '' @z.times { |i| s << @board[x+i][y+i] } ret = check(s) and return ret end # check diagonally up and to the right if @cols - y >= @z and (x+1) - @z >= 0 s = '' @z.times { |i| s << @board[x-i][y+i] } ret = check(s) and return ret end end end false end def other(player) player == @x ? @o : @x end def loser return false unless winner other(winner) end # assumption: winner == false def draw @board.each {|row| return false if row.include? ?_} true end def gameover return true if winner return true if draw end def make_move(player,move) return false unless player == @x or player == @o return false unless move =~ /^\s*(\d+),(\d+)\s*$/ x,y = $1.to_i,$2.to_i return false if x >= @cols or x < 0 or y >= @rows or y < 0 token = player == @x ? "X" : "O" return false unless @board[x][y] == ?_ @board[x][y] = token true end def serve_game reset begin hello = "fugal.net Tic-Tac-Toe server" @x = self.accept @x.puts hello @x.puts "X #{@rows}x#{@cols}" @x.gets @o = self.accept @o.puts hello @o.puts "O #{@rows}x#{@cols}" @o.gets cur = @x until gameover $stdout.puts state.split(' ').join("\n") $stdout.puts [@x,@o].each {|p| p.puts state} move = nil until move cur.puts "move" move = cur.gets move = nil unless make_move(cur,move) end cur = other(cur) end $stdout.puts state.split(' ').join("\n") $stdout.puts [@x,@o].each {|p| p.puts state} if draw [@x,@o].each {|p| p.puts 'draw'} else winner.puts "win" other(winner).puts "lose" end ensure [@x,@o].each {|p| p.close if p} end end end if $0 == __FILE__ port = (ARGV[0] || 1276).to_i x = (ARGV[1] || 3).to_i y = (ARGV[2] || 3).to_i z = (ARGV[3] || 3).to_i puts "server = TicTacToeServer.new(#{port},#{x},#{y},#{z})" server = TicTacToeServer.new(port,x,y) while true server.serve_game rescue nil end end --------------000008000504010303080605--