-
-
Save dsisnero/7e98fe2cfc820cfb974348512e87b07d to your computer and use it in GitHub Desktop.
othello game with Shoes
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
require "drb/drb" | |
module Othero | |
class Board | |
include Enumerable | |
attr_reader :size | |
Size = {:small => 6, :medium => 8, :large => 10} | |
def initialize(size= :small) | |
@size = Size[size] | |
@cells = Array.new(@size){ Array.new(@size) } | |
end | |
def [](col, row) | |
@cells[col-1][row-1] | |
end | |
def []=(col, row, *colors) | |
if colors[0] | |
@cells[col-1][row-1] = Piece.new(col, row, *colors) | |
else | |
@cells[col-1][row-1] = nil | |
end | |
end | |
def to_s | |
@cells.flatten | |
end | |
def each | |
@cells.each { |cols| cols.each { |cell| yield cell } } | |
end | |
def each_with_square_index | |
@cells.each_with_index { |cols, i| cols.each_with_index { |cell, j| yield(cell, i+1, j+1) } } | |
end | |
def filled_cells | |
select_cells { |c| c } | |
end | |
def empty_cells | |
select_cells { |c| c.nil? } | |
end | |
private | |
def select_cells | |
result = [] | |
self.each_with_square_index { |cell, col, row| result << [col, row] if yield cell } | |
result | |
end | |
end | |
class Piece | |
attr_reader :col, :row, :face, :back | |
def initialize(col, row, colors) | |
@col, @row = col, row | |
@face, @back = *colors | |
end | |
def to_s | |
@face.to_s | |
end | |
def flip | |
@face, @back = @back, @face | |
end | |
end | |
end | |
module Draw | |
def initial_set | |
c = Board.size/2 | |
Board[c, c] = COLORS | |
Board[c+1, c+1] = COLORS | |
Board[c+1, c] = COLORS.flip | |
Board[c, c+1] = COLORS.flip | |
draw_board("Start Game!") | |
end | |
def draw_board(msg='') | |
clear do | |
background BASE_COLOR | |
stack :margin => MARGIN do | |
fill BOARD_COLOR | |
rect :left => 0, :top => 0, :width => BOARD_SIZE, :height => BOARD_SIZE | |
end | |
Board.each_with_square_index do |piece, col, row| | |
left, top = (row-1)*CELL_WIDTH+MARGIN, (col-1)*CELL_HEIGHT+MARGIN | |
fill BOARD_COLOR; strokewidth 1; stroke rgb(0, 0, 0) | |
rect :left => left, :top => top, :width => CELL_WIDTH, :height => CELL_HEIGHT | |
if piece | |
strokewidth 0 | |
fill (piece.face == COLORS[1] ? rgb(155,155,155) : rgb(100,100,100)) | |
oval left+5, top+6, CELL_WIDTH-10, CELL_HEIGHT-10 | |
fill (piece.face == COLORS[1] ? COLOR_SET[1] : COLOR_SET[0]) | |
oval left+4, top+4, CELL_WIDTH-10, CELL_HEIGHT-10 | |
else | |
fill BOARD_COLOR | |
rect :left => left, :top => top, :width => CELL_WIDTH, :height => CELL_HEIGHT | |
end | |
end | |
draw_message_board(msg) | |
end | |
end | |
def draw_message_board(msg) | |
pt1, pt2, elapsed = current_status | |
stack :top => BOARD_SIZE+MARGIN, :left => 1, :margin => MARGIN do | |
background MSBOARD_BGCOLOR | |
para "#{msg} #{@flag ? COLORS[0] : COLORS[1]} turn. #{COLORS[0].to_s[0,1]}#{pt1} - #{COLORS[1].to_s[0,1]}#{pt2}", :stroke => MSBOARD_PENCOLOR | |
end | |
end | |
def find_cell(left, top) | |
while left.between?(MARGIN, MARGIN+BOARD_SIZE) and top.between?(MARGIN, MARGIN+BOARD_SIZE) | |
return (top-MARGIN)/CELL_HEIGHT+1, (left-MARGIN)/CELL_WIDTH+1 | |
end | |
end | |
def current_status | |
cnt_c1, cnt_c2 = 0, 0 | |
Board.each do |piece| | |
next unless piece | |
case piece.face | |
when COLORS[0] | |
cnt_c1 += 1 | |
when COLORS[1] | |
cnt_c2 += 1 | |
end | |
end | |
elapsed = (cnt_c1+cnt_c2)*100/Board.size**2 | |
[cnt_c1, cnt_c2, elapsed] | |
end | |
def alert_winner | |
if who = winner? | |
pt1, pt2 = current_status | |
if who | |
alert "#{COLORS[who]} wins!! by +#{(pt1-pt2).abs}" | |
else | |
alert "Tie Game" | |
end | |
break | |
end | |
end | |
def winner? | |
cnt_c1, cnt_c2 = current_status | |
if (cnt_c1+cnt_c2) == Board.size**2 or (!flippable?(COLORS) and !flippable?(COLORS.flip)) | |
if cnt_c1 > cnt_c2 then 0 | |
elsif cnt_c1 < cnt_c2 then 1 | |
else -1 | |
end | |
else | |
nil | |
end | |
end | |
def flip_around?(piece, flip=false) | |
flag = [] | |
[:E, :W, :S, :N, :NE, :NW, :SE, :SW].each do |dir| | |
pieces = flippable_line(piece, dir) | |
flag << pieces | |
pieces.each { |p| p.flip } if flip | |
end | |
!flag.all?{ |p| p.empty? } | |
end | |
def alert_flippable | |
next_color = @flag ? COLORS : COLORS.flip | |
unless flippable?(next_color) | |
alert("#{next_color[0]} can't flip any pieces!\n Wait next turn") | |
@flag = !@flag | |
draw_message_board("") | |
end | |
end | |
def flippable?(color) | |
Board.empty_cells.each do |col, row| | |
if flip_around?(Othero::Piece.new(col, row, color)) | |
return true | |
end | |
end | |
false | |
end | |
def lay_piece(col, row) | |
unless Board[col, row] | |
Board[col, row] = @flag ? COLORS : COLORS.flip | |
@msg = if flip_around?(Board[col, row], true) | |
@flag = !@flag | |
"Yeh! Good one!" | |
else | |
Board[col, row] = nil | |
"Wrong place!" | |
end | |
end | |
end | |
private | |
def flippable_line(piece, dir) | |
col, row = piece.col, piece.row | |
case dir | |
when :E | |
check_line(piece, col, row, op(:+, 0), op(:+, 1)) | |
when :W | |
check_line(piece, col, row, op(:+, 0), op(:-, 1)) | |
when :S | |
check_line(piece, col, row, op(:+, 1), op(:+, 0)) | |
when :N | |
check_line(piece, col, row, op(:-, 1), op(:+, 0)) | |
when :NE | |
check_line(piece, col, row, op(:-, 1), op(:+, 1)) | |
when :NW | |
check_line(piece, col, row, op(:-, 1), op(:-, 1)) | |
when :SE | |
check_line(piece, col, row, op(:+, 1), op(:+, 1)) | |
when :SW | |
check_line(piece, col, row, op(:+, 1), op(:-, 1)) | |
end | |
end | |
def op(op, arg) | |
lambda { |x| x.send(op, arg) } | |
end | |
def check_line(piece, col, row, op_col, op_row) | |
col = op_col[col]; row = op_row[row] | |
list = [] | |
while col.between?(1, Board.size) and row.between?(1, Board.size) | |
if Board[col, row].nil? | |
return list.clear | |
elsif Board[col, row].face == piece.face | |
return list | |
else | |
list << Board[col, row] | |
end | |
col = op_col[col]; row = op_row[row] | |
end | |
list.clear | |
end | |
end | |
class Server | |
def initialize(owner) | |
@owners = [owner] | |
end | |
def join_server(owner) | |
@owners << owner | |
end | |
def send_click(me, col, row) | |
@owners.each do |owner| | |
unless owner == me | |
owner.lay_piece(col, row) | |
owner.draw_board | |
end | |
end | |
end | |
def send_release(me, col, row) | |
@owners.each do |owner| | |
unless owner == me | |
owner.alert_winner | |
owner.alert_flippable | |
end | |
end | |
end | |
end | |
BOARD_SIZE = 400 | |
MARGIN = 5 | |
APP_WIDTH = BOARD_SIZE + MARGIN*2 | |
APP_HEIGHT = BOARD_SIZE + 90 | |
URI = "druby://127.0.0.1:8787" | |
Shoes.app :width => APP_WIDTH, :height => APP_HEIGHT, :resizable => false do | |
extend Draw | |
Board = Othero::Board.new(:medium) | |
CELL_WIDTH, CELL_HEIGHT = BOARD_SIZE/Board.size, BOARD_SIZE/Board.size | |
BASE_COLOR = rgb(0, 0, 50) | |
BOARD_COLOR = rgb(0, 100, 200) | |
MSBOARD_BGCOLOR = darkgreen | |
MSBOARD_PENCOLOR = gold | |
COLORS = [:Black, :White] | |
COLOR_SET = [rgb(0, 0, 30), cornsilk] | |
def COLORS.flip | |
self.reverse | |
end | |
@flag = true | |
initial_set | |
click do |button, left, top| | |
col, row = find_cell(left, top) | |
msg = lay_piece(col, row) | |
draw_board(msg) | |
begin | |
FRONT.send_click(self, col, row) | |
rescue DRb.DRbError | |
alert('server down!') | |
end | |
end | |
release do | |
alert_winner | |
alert_flippable | |
begin | |
FRONT.send_release | |
rescue DRb.DRbError | |
alert('server down!') | |
end | |
end | |
flow do | |
@side_msg = para "Select a color:", :stroke => white | |
@btn1 = button "#{COLORS[0]}" do | |
FRONT = Server.new(self) | |
drb = DRb.start_service(URI, FRONT) | |
@side_msg.replace "You are #{COLORS[0]}." | |
@btn1.remove; @btn2.remove | |
end | |
@btn2 = button "#{COLORS[1]}" do | |
begin | |
drb = DRb.start_service | |
FRONT = DRbObject.new_with_uri(URI) | |
FRONT.join_server(self) | |
@side_msg.replace "You are #{COLORS[1]}." | |
rescue StandardError => err | |
alert("Error connecting to server:\n#{err}") | |
end | |
@btn1.remove; @btn2.remove | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment