From: Ruby Quiz Date: 2005-12-09T22:27:31+09:00 Subject: [QUIZ] Kalah (#58) The three rules of Ruby Quiz: 1. Please do not post any solutions or spoiler discussion for this quiz until 48 hours have passed from the time on this message. 2. Support Ruby Quiz by submitting ideas as often as you can: http://www.rubyquiz.com/ 3. Enjoy! -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= by Mark Van Holstyn For my Computer Science class this month, we had to program a Kalah Player. Sadly, we had to do our project in Java, but I ported the game code and the sample players to ruby for this quiz though. A good explanation of the game can be found at: http://rubyurl.com/SqU Your task is to create the best Kalah player you can. The player should look like the following: class HumanPlayer < Player def choose_move print 'Enter your move choice: ' gets.chomp.to_i end end The 'choose_move' method is what the game will call to request a move from the player. The Player class which you player should extend looks like so: class Player attr_accessor :name attr_writer :game, :side def initialize( name ) @name = name end def choose_move if @side==KalahGame::TOP (7..12).each { |i| return i if @game.stones_at?(i) > 0 } else (0..5).each { |i| return i if @game.stones_at?(i) > 0 } end end end The 'game' and 'side' attributes will be assigned by KalahGame. The side will either be the constant KalahGame::TOP or KalahGame::BOTTOM. This is what you will use to determine if you should be making moves from bowls 0-5 or 7-12 and whether your Kalah is 6 or 13. KalahMatch plays two games, one with each player on the each side of the board and then averages their scores, so you must make you player be able to play both on the top and the bottom. Here's a sample game engine to get you started: require 'Player' require 'HumanPlayer' class KalahMatch def start( p1, p2 ) puts '' puts '========== GAME 1 ==========' p1_score_1, p2_score_1 = KalahGame.new.play_game( p1, p2 ) if p1_score_1 > p2_score_1 puts p1.name+' won game #1: '+p1_score_1.to_s+'-'+p2_score_1.to_s elsif p2_score_1 > p1_score_1 puts p2.name+' won game #1: '+p2_score_1.to_s+'-'+p1_score_1.to_s else puts 'game #1 was a tie: '+p1_score_1.to_s+'-'+p2_score_1.to_s end puts '' puts '========== GAME 2 ==========' p2_score_2, p1_score_2 = KalahGame.new.play_game( p2, p1 ) if p1_score_2 > p2_score_2 puts p1.name+' won game #2: '+p1_score_2.to_s+'-'+p2_score_2.to_s elsif p2_score_2 > p1_score_2 puts p2.name+' won game #2: '+p2_score_2.to_s+'-'+p1_score_2.to_s else puts 'game #2 was a tie: '+p1_score_2.to_s+'-'+p2_score_2.to_s end puts '' puts '========== FINAL ==========' p1_final = p1_score_1+p1_score_2 p2_final = p2_score_1+p2_score_2 if p1_final > p2_final puts p1.name+' won the match: '+p1_final.to_s+'-'+p2_final.to_s elsif p2_final > p1_final puts p2.name+' won the match: '+p2_final.to_s+'-'+p1_final.to_s else puts 'the match was tied overall : '+p1_final.to_s+'-'+p2_final.to_s end end end class KalahGame NOBODY = 0 TOP = 1 BOTTOM = 2 def stones_at?( i ) @board[i] end def legal_move?( move ) ( ( @player_to_move==TOP and move >= 7 and move <= 12 ) || ( @player_to_move==BOTTOM and move >= 0 and move <= 5 ) ) and @board[move] != 0 end def game_over? top = bottom = true (7..12).each { |i| top = false if @board[i] > 0 } (0..5).each { |i| bottom = false if @board[i] > 0 } top or bottom end def winner top, bottom = top_score, bottom_score if top > bottm return TOP elsif bottom > top return BOTTOM else return NOBODY end end def top_score score = 0 (7..13).each { |i| score += @board[i] } score end def bottom_score score = 0 (0..6).each { |i| score += @board[i] } score end def make_move( move ) ( puts 'Illegal move...' ; return ) unless legal_move?( move ) stones, @board[move] = @board[move], 0 pos = move+1 while stones > 0 pos+=1 if( (@player_to_move==TOP and pos==6) || (@player_to_move==BOTTOM and pos==13) ) pos = 0 if pos==14 @board[pos]+=1 stones-=1 pos+=1 if stones > 0 end if( @player_to_move==TOP and pos>6 and pos<13 and @board[pos]==1 ) @board[13] += @board[12-pos]+1 @board[12-pos] = @board[pos] = 0 elsif( @player_to_move==BOTTOM and pos>=0 and pos<6 and @board[pos]==1 ) @board[6] += @board[12-pos]+1 @board[12-pos] = @board[pos] = 0 end if @player_to_move==TOP @player_to_move = BOTTOM unless pos == 13 else @player_to_move=TOP unless pos == 6 end end def display puts '' top = ' ' [12,11,10,9,8,7].each { |i| top += @board[i].to_s+' ' } puts top puts @board[13].to_s + ' ' + @board[6].to_s bottom = ' ' (0..5).each { |i| bottom += @board[i].to_s+' ' } puts bottom puts '' end def reset @board = Array.new( 14, 4 ) @board[6] = @board[13] = 0 @player_to_move = BOTTOM end def play_game( bottom, top ) reset bottom.side = BOTTOM top.side = TOP top.game = bottom.game = self puts bottom.name+' starts...' display until game_over? puts '' if @player_to_move == TOP move = top.choose_move puts top.name+' choose move '+move.to_s else move = bottom.choose_move puts bottom.name+' choose move '+move.to_s end make_move( move ) display end [top_score, bottom_score] end end p1 = Player.new( 'Player 1' ) p2 = Player.new( 'Player 2' ) KalahMatch.new.start( p1, p2 )