Skip to content
This repository has been archived by the owner on Dec 12, 2021. It is now read-only.

Commit

Permalink
major refactoring to game engine and worker
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanb committed Oct 22, 2010
1 parent 95e0f14 commit 229c144
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 251 deletions.
66 changes: 13 additions & 53 deletions app/models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ def white_positions_list
@white_positions_list ||= white_positions.to_s.scan(/[a-s]{2}/)
end

def valid_positions_list
valid_positions.to_s.scan(/[a-s]{2}/) + %w[PASS RESIGN]
end

def prepare
opponent = nil
if chosen_opponent == "user"
Expand All @@ -101,7 +97,6 @@ def prepare
self.white_player = creator
end
game_engine do |engine|
self.valid_positions = engine.legal_moves(:black)
if handicap.to_i.nonzero?
self.black_positions = engine.positions(:black)
self.current_player = white_player
Expand All @@ -115,42 +110,30 @@ def prepare
def move(vertex, user)
raise GameEngine::OutOfTurn if user.id != current_player_id
game_engine do |engine|
engine.replay(moves, first_color)
played = engine.move(current_color, vertex)
self.valid_positions = engine.legal_moves(next_color)
engine.replay(moves)
self.current_player = next_player
if vertex == "RESIGN"
finish_game(engine.final_score)
elsif vertex == "PASS" && moves =~ /-\z/
self.moves = moves.blank? ? played : [moves, ""].join("-")
finish_game(engine.final_score)
self.moves = [moves, engine.move(current_color, vertex)].reject(&:blank?).join("-")
self.black_positions = engine.positions(:black)
self.white_positions = engine.positions(:white)
if engine.game_finished?
self.finished_at = Time.now
self.black_score = engine.black_score
self.white_score = engine.white_score
else
played = "" if vertex == "PASS"
self.moves = moves.blank? ? played : [moves, played].join("-")
self.black_positions = engine.positions(:black)
self.white_positions = engine.positions(:white)
self.black_score = engine.captures(:black)
self.white_score = engine.captures(:white)
self.position_changed = true
end
end
# Check current_player again, fetching from database to avoid double moves
# Check current_player again, fetching from database to async double move problem
# This should probably be moved into a database lock so no updates happen between here and the save
raise GameEngine::OutOfTurn if user.id != Game.find(id, :select => "current_player_id").current_player_id
save!
end

def queue_computer_move
unless current_player_is_human?
Stalker.enqueue( "Game.move",
:id => id,
:next_player_id => next_player.id,
:boardsize => board_size,
:handicap => handicap,
:komi => komi,
:moves_for_gnugo => GameEngine.sgf_to_gnugo(moves, board_size),
:moves_for_db => moves,
:first_color => first_color,
:current_color => current_color )
Stalker.enqueue("Game.move", :id => id, :next_player_id => next_player.id, :current_color => current_color)
end
end

Expand All @@ -162,10 +145,6 @@ def last_move
(moves.to_s.split("-").last || "")[/\A[a-s]{2}/]
end

def first_color
handicap.to_i.nonzero? ? "white" : "black"
end

def current_color
current_player == black_player ? "black" : "white"
end
Expand All @@ -178,24 +157,10 @@ def next_player
current_player == black_player ? white_player : black_player
end

def finish_game(final_score)
self.finished_at = Time.now
if final_score =~ /\A([BW])\+(\d+\.\d+)\z/
send("#{$1 == 'B' ? :black_score : :white_score}=", $2.to_f)
send("#{$1 == 'B' ? :white_score : :black_score}=", 0)
else
raise "Unrecognized score format: #{final_score}"
end
end

def finished?
not finished_at.blank?
end

def resigned?
finished? && moves =~ /-{2}\z/
end

def black_player_name
profile_for(:black).name
end
Expand All @@ -206,12 +171,7 @@ def white_player_name

def update_thumbnail
if Rails.env != "test" && position_changed?
GameThumb.generate( id,
board_size,
black_positions_list
.map { |ln| Go::GTP::Point.new(ln).to_indices },
white_positions_list
.map { |ln| Go::GTP::Point.new(ln).to_indices } )
GameThumb.generate(id, board_size, black_positions, white_positions)
end
end

Expand All @@ -234,7 +194,7 @@ def profiles
private

def game_engine
GameEngine.run(:boardsize => board_size, :handicap => handicap, :komi => komi) do |engine|
GameEngine.run(:board_size => board_size, :handicap => handicap, :komi => komi) do |engine|
yield engine
end
end
Expand Down
92 changes: 0 additions & 92 deletions app/models/game_engine.rb

This file was deleted.

8 changes: 4 additions & 4 deletions app/views/moves/create.js.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% if flash[:alert] %>
alert("<%= escape_javascript(flash.delete(:alert)) %>");
<% else %>
addMoves("<%= @game.moves_after(params[:after].to_i) %>", "<%= @game.current_player_id.to_i %>");
$('.profile .details #score_<%= @game.black_player_id.to_i %>').html(<%= raw game_score(@game.black_score, @game.finished?).to_s.inspect %>);
$('.profile .details #score_<%= @game.white_player_id.to_i %>').html(<%= raw game_score(@game.white_score, @game.finished?).to_s.inspect %>);
<% end %>
addMoves("<%= escape_javascript(@game.moves_after(params[:after].to_i)) %>", "<%= @game.current_player_id.to_i %>");
$('.profile .details #score_<%= @game.black_player_id.to_i %>').html("<%= escape_javascript(game_score(@game.black_score, @game.finished?).to_s) %>");
$('.profile .details #score_<%= @game.white_player_id.to_i %>').html("<%= escape_javascript(game_score(@game.white_score, @game.finished?).to_s) %>");
<% end %>
8 changes: 3 additions & 5 deletions app/views/moves/index.js.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<% if @moves.blank? %>
setTimeout(pollMoves, pollTimer);
<% elsif @game.resigned? %>
alert("<%= @game.black_score.zero? ? "Black" : "White" %> resigned.");
<% else %>
addMoves("<%= @moves %>", "<%= @game.current_player_id.to_i %>");
$('.profile .details #score_<%= @game.black_player_id.to_i %>').html(<%= raw game_score(@game.black_score, @game.finished?).to_s.inspect %>);
$('.profile .details #score_<%= @game.white_player_id.to_i %>').html(<%= raw game_score(@game.white_score, @game.finished?).to_s.inspect %>);
addMoves("<%= escape_javascript(@moves) %>", "<%= @game.current_player_id.to_i %>");
$('.profile .details #score_<%= @game.black_player_id.to_i %>').html("<%= escape_javascript(game_score(@game.black_score, @game.finished?).to_s) %>");
$('.profile .details #score_<%= @game.white_player_id.to_i %>').html("<%= escape_javascript(game_score(@game.white_score, @game.finished?).to_s) %>");
<% end %>
107 changes: 107 additions & 0 deletions lib/game_engine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
class GameEngine
class Error < StandardError; end
class IllegalMove < Error; end
class OutOfTurn < Error; end

def self.run(options = {}, &block)
Go::GTP.run_gnugo do |gtp|
gtp.boardsize(options[:board_size]) unless options[:board_size].to_s.empty?
gtp.fixed_handicap(options[:handicap]) if options[:handicap].to_i.nonzero?
gtp.komi(options[:komi]) unless options[:komi].to_s.empty?
yield GameEngine.new(gtp, options)
end
end

def initialize(gtp, options = {})
@gtp = gtp
@board_size = options[:board_size] || 19
@handicap = options[:handicap] || 0
end

def replay(moves)
colors = [:black, :white].cycle
colors.next if first_color == :white
moves.to_s.split("-").each do |move|
play(colors.next, (move =~ /[A-Z]/ ? move : move[0..1]))
end
end

def play(color, vertex)
@gtp.play(color, gnugo_point(vertex))
raise IllegalMove unless @gtp.success?
end

def move(color, vertex = nil)
if %w[PASS RESIGN].include? vertex
play(color, vertex)
vertex
else
other_stones = @gtp.list_stones(opposite(color))
if vertex
play(color, vertex)
else
vertex = sgf_point(@gtp.genmove(color))
end
captured = other_stones - @gtp.list_stones(opposite(color))
sgf_point(vertex) + captured.map { |v| sgf_point(v) }.join
end
end

def positions(color)
@gtp.list_stones(color).map { |v| sgf_point(v) }.join
end

def captures(color)
@gtp.captures(color)
end

def black_score
score_for("B")
end

def white_score
score_for("W")
end

def game_finished?
@gtp.over?
end

def first_color
@handicap > 0 ? :white : :black
end

private

def score_for(color)
@gtp.final_score[/^#{color}\+([\d\.]+)$/, 1].to_f
end

def opposite(color)
color == :black ? :white : :black
end

def point(vertex)
args = [vertex]
if vertex =~ /\A[A-HJ-T](?:1\d|[1-9])\z/
args << {:board_size => @board_size}
end
Go::GTP::Point.new(*args)
end

def gnugo_point(vertex)
if %w[PASS RESIGN].include? vertex
vertex
else
point(vertex).to_gnugo(@board_size)
end
end

def sgf_point(vertex)
if %w[PASS RESIGN].include? vertex
vertex
else
point(vertex).to_sgf
end
end
end
21 changes: 11 additions & 10 deletions lib/game_thumb.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
require "fileutils"

module GameThumb
IMAGE_DIR = File.join( File.dirname(__FILE__),
*%w[.. public images thumbnail] )
THUMB_DIR = File.join( File.dirname(__FILE__),
*%w[.. public assets games thumbs] )
IMAGE_DIR = File.join(File.dirname(__FILE__), *%w[.. public images thumbnail])
THUMB_DIR = File.join(File.dirname(__FILE__), *%w[.. public assets games thumbs])

module_function

Expand All @@ -15,16 +13,19 @@ def generate(id, size, black_positions, white_positions)
white = ChunkyPNG::Image.from_file(File.join(images, "white_stone.png"))
offset = 76 / size.to_f

black_positions.each do |x, y|
board.compose(black, (x * offset).round, (y * offset).round)
end
white_positions.each do |x, y|
board.compose(white, (x * offset).round, (y * offset).round)
end
add_stones(board, black, black_positions, offset)
add_stones(board, white, white_positions, offset)

FileUtils.mkdir_p(THUMB_DIR)
thumb = File.join(THUMB_DIR, "#{id}.png")
board.save("#{thumb}~", :fast_rgba)
FileUtils.mv("#{thumb}~", thumb)
end

def add_stones(board, stone, positions, offset)
positions.to_s.scan(/[a-s]{2}/).each do |position|
x, y = Go::GTP::Point.new(position).to_indices
# board.compose(stone, (x * offset).round, (y * offset).round)
end
end
end
Loading

0 comments on commit 229c144

Please sign in to comment.