From: "Hal E. Fulton" Date: 2001-09-26T14:58:47+09:00 Subject: [ruby-talk:21708] Repost. Final call for help on this piece of code... *sigh* Sorry for the waste of bandwidth. Trying to figure this out or I'll have to abandon it. HF =================================== Hello all... I've been through numerous iterations with this thing. I hope the code doesn't show it, but I'm afraid it does. Any help would be appreciated. In this email: 1. overview 2. how to invoke 3. server code 4. client code Besides net difficulties, it's likely there's a logic error or two. The code *does* work on a single machine (both Win98 and Solaris). Thanks for any insights... Hal Fulton === 1. Overview ----------------- This is a stubbed chess server that does nothing but put clients in contact with each other so they can play chess. The clients then talk directly and ignore the server. (Then one client is really a server henceforth.) At first I was using UDP for the initial contact, but changed to TCP... this helped a little. The server and each client should ideally all be run on separate machines for good testing. 2. How to invoke ----------------------- a. Change ChessServer constant in ch2c.rb b. Invoke server: ruby ch2s.rb c. Invoke 1st client (with any two names -- mine and the opponent); a colon separates the opponent's name from his IP name (localhost is the default) ruby ch2c.rb Alice Bob:machine2 d. Invoke 2nd client in the corresponding way ruby ch2c.rb Bob Alice:machine3 e. When prompted for a move, type any arbitrary string... it's stubbed out. f. In this dummy version, black always wins on the fourth move 3. Server code ---------------------- require "thread" require "socket" PORT = 12000 HOST = '127.0.0.1' # Exit if user presses Enter at server end waiter = Thread.new do puts "Press Enter to exit the server." gets exit end $mutex = Mutex.new $list = {} def match?(p1, p2) return false if !$list[p1] or !$list[p2] if ($list[p1][0] == p2 and $list[p2][0] == p1) true else false end end def handle_client(sess, msg, addr, port, ipname) $mutex.synchronize do cmd, player1, player2 = msg.split # Note: We allow user:hostname on the command line, # but we store it in the form user:address p1short = player1.dup # Short names (i.e., p2short = player2.split(":")[0] # no ":address" player1 << ":#{addr}" # Append user's IP addr user2, host2 = player2.split(":") if host2 == nil # No IP addr; add it player2 << ":127.0.0.1" else player2 = user2 + ":" + IPSocket.getaddress(host2) end if cmd != "login" puts "Protocol error: client msg was #{msg}" end $list[player1] = [player2, addr, port, ipname, sess] print "hc: " p $list if match?(player1, player2) puts "Got a match!" # Note these names are "backwards" now: player2 # logged in first, if we got here. p1 = $list[player1] p2 = $list[player2] # Player ID = name:ipname:color # Color: 0=white, 1=black p1id = "#{p1short}:#{p1[3]}:1" p2id = "#{p2short}:#{p2[3]}:0" sess1 = p1[4] sess2 = p2[4] sess1.puts "#{p2id}" sess2.puts "#{p1id}" sess1.close sess2.close end end end text = nil $server = TCPServer.new("127.0.0.1", PORT) while session = $server.accept do Thread.new(session) do |sess| text = sess.gets puts "recvfrom = #{text}" domain, port, ipname, ipaddr = sess.addr puts "calling hc" handle_client sess, text, ipaddr, port, ipname puts "called hc" sleep 1 end end waiter.join # Exit only if user presses Enter 4. Client -------------- require "socket" require "timeout" ChessServer = 'localhost' ChessServerPort = 12000 PeerPort = 12001 WHITE, BLACK = 0, 1 Colors = %w[White Black] def drawBoard(board) puts <<-EOF +----------------------------------+ | Stub! Drawing the board here... | +----------------------------------+ EOF end def analyzeMove(who, move, num, board) # Stub - black always wins on 4th move if who == BLACK and num == 4 move << " Checkmate!" end true # Stub again - always say it's legal. end def myMove(who, lastmove, num, board, sock) ok = false until ok do print "\nYour move: " move = STDIN.gets.chomp ok = analyzeMove(who, move, num, board) puts "Illegal move" if not ok end sock.puts move move end def otherMove(who, move, num, board, sock) move = sock.gets.chomp puts "\nOpponent: #{move}" move end if ARGV[0] myself = ARGV[0] else print "Your name? " myself = STDIN.gets.chomp end if ARGV[1] opponentID = ARGV[1] else print "Your opponent? " opponentID = STDIN.gets.chomp end opponent = opponentID.split(":")[0] # Remove hostname # Contact the server socket = TCPSocket.new(ChessServer, ChessServerPort) response = nil puts "Sending login to server" socket.puts "login #{myself} #{opponentID}" socket.flush puts "Waiting for response from server" response = socket.gets.chomp puts "Got response #{response} from server" name, ipname, color = response.split ":" color = color.to_i if color == BLACK # Other player's color puts "\nConnecting..." server = TCPServer.new(ipname, PeerPort) session = server.accept str = nil begin timeout(30) do str = session.gets.chomp if str != "ready" raise "Protocol error: ready-message was #{str}" end end rescue TimeoutError raise "Did not get ready-message from opponent." end puts "Playing #{opponent}... you are white.\n" who = WHITE move = nil board = nil # Not really used in this dummy example num = 0 drawBoard(board) # Draw the board initially for white loop do num += 1 move = myMove(who, move, num, board, session) drawBoard(board) case move when "resign" puts "\nYou've resigned. #{opponent} wins." break when /Checkmate/ puts "\nYou have checkmated #{opponent}!" drawBoard(board) break end move = otherMove(who, move, num, board, session) drawBoard(board) case move when "resign" puts "\n#{opponent} has resigned... you win!" break when /Checkmate/ puts "\n#{opponent} has checkmated you." break end end else # We're black puts "\nConnecting..." socket = TCPSocket.new(ipname, PeerPort) socket.puts "ready" puts "Playing #{opponent}... you are black.\n" who = BLACK move = nil board = nil # Not really used in this dummy example num = 0 drawBoard(board) # Draw board initially loop do num += 1 move = otherMove(who, move, num, board, socket) drawBoard(board) # Draw board after white move case move when "resign" puts "\n#{opponent} has resigned... you win!" break when /Checkmate/ puts "\n#{opponent} has checkmated you." break end move = myMove(who, move, num, board, socket) drawBoard(board) case move when "resign" puts "\nYou've resigned. #{opponent} wins." break when /Checkmate/ puts "\nYou have checkmated #{opponent}!" break end end socket.close end