Last active
August 29, 2015 13:59
-
-
Save myokoym/10743703 to your computer and use it in GitHub Desktop.
A Shogi board using Gosu and Ruby. (under construction...)
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 "gosu" | |
require "shogi" | |
require "socket" | |
module RbShogi | |
PIECES = [ | |
"FU", "TO", | |
"KY", "NY", | |
"KE", "NK", | |
"GI", "NG", | |
"KI", | |
"KA", "UM", | |
"HI", "RY", | |
"OU", | |
] | |
class Piece | |
class << self | |
def init(window) | |
PIECES.each do |piece| | |
next if const_defined?(piece) | |
const_set(piece, | |
Gosu::Image.new(window, | |
File.join(image_dir, "#{piece}.png"), | |
false)) | |
end | |
end | |
def image_dir | |
File.join(File.expand_path(File.dirname(__FILE__)), "images") | |
end | |
end | |
end | |
class Text | |
class << self | |
def init(window) | |
1.upto(18) do |i| | |
name = "N#{i}" | |
next if const_defined?(name) | |
const_set(name, | |
Gosu::Image.from_text(window, | |
i.to_s, | |
Gosu.default_font_name, | |
window.height / 10)) | |
end | |
end | |
end | |
end | |
class Cell | |
attr_reader :piece | |
attr_reader :row, :column | |
def initialize(window, x1, y1, row, column, width, height, piece=nil) | |
@window = window | |
@x1 = x1 | |
@y1 = y1 | |
@width = width | |
@height = height | |
@x2 = @x1 + width | |
@y2 = @y1 + height | |
@row = row | |
@piece = piece | |
@column = column | |
end | |
def in?(x, y) | |
@x1 < x && @x2 > x && | |
@y1 < y && @y2 > y | |
end | |
def to_csa | |
"#{9 - @row}#{@column + 1}" | |
end | |
end | |
class Tray | |
def initialize(window, x1, y1, x2, y2, has) | |
@window = window | |
@x1 = x1 | |
@y1 = y1 | |
@x2 = x2 | |
@y2 = y2 | |
@width = @x2 - @x1 | |
@height = @y2 - @y1 | |
@background_color = Gosu::Color.new(0xffffffcc) | |
@has = has | |
@cells = [] | |
create_cells | |
end | |
def create_cells | |
width = @width | |
height = @height / 8 | |
%w(FU KY KE GI KI KA HI OU).each_with_index do |piece, i| | |
@cells << Cell.new(@window, | |
@x1, | |
@y1 + height * i, | |
9, | |
-1, | |
width, | |
height, | |
"#{@has}#{piece}") | |
end | |
end | |
def in?(x, y) | |
@x1 < x && @x2 > x && | |
@y1 < y && @y2 > y | |
end | |
def at(x, y) | |
@cells.find {|cell| cell.in?(x, y) } | |
end | |
def draw_pieces(pieces) | |
has = pieces[1] | |
num_of_pieces = {} | |
%w(FU KY KE GI KI KA HI OU).each do |piece| | |
num_of_pieces["#{has}#{piece}"] = 0 | |
end | |
width = @width | |
height = @height / 8 | |
pieces[2..-1].scan(/.{4}/).each_with_index do |piece, i| | |
num_of_pieces["#{has}#{piece[2..3]}"] += 1 | |
end | |
num_of_pieces.each_with_index do |entry, i| | |
piece = entry[0] | |
count = entry[1] | |
next unless count > 0 | |
has = piece[0] | |
type = piece[1..2] | |
angle = (has == "+") ? 0 : 180 | |
x = @x1 + width * 0.4 | |
y = @y1 + i * height + height * 0.5 | |
image = Piece.const_get(type) | |
image.draw_rot(x, y, 2, | |
angle, 0.5, 0.5, | |
0.4 * width / image.width, | |
0.8 * height / image.height) | |
text = Text.const_get("N#{count}") | |
x = @x1 + width * 0.6 | |
y = @y1 + i * height + height * 0.4 | |
text.draw(x, y, 2, | |
0.5 * width / image.width, | |
1.0 * height / image.height, | |
Gosu::Color::BLACK) | |
end | |
end | |
def draw_background | |
@window.draw_quad(@x1, @y1, @background_color, | |
@x2, @y1, @background_color, | |
@x2, @y2, @background_color, | |
@x1, @y2, @background_color, | |
0) | |
end | |
end | |
class Board | |
def initialize(window, x1, y1, width, height) | |
@window = window | |
@x1 = x1 | |
@y1 = y1 | |
@width = width | |
@height = height | |
@x2 = @x1 + width | |
@y2 = @y1 + height | |
@background_color = Gosu::Color.new(0xffffffcc) | |
Piece.init(@window) | |
Text.init(@window) | |
@board = Shogi::Board.new | |
@board.validate_movement = false | |
@cells = [] | |
create_cells | |
@top_tray = Tray.new(@window, | |
@x1 - @width * 0.3, @y1, | |
@x1 - @width * 0.03, @y2, | |
"-") | |
@bottom_tray = Tray.new(@window, | |
@x2 + @width * 0.03, @y1, | |
@x2 + @width * 0.3, @y2, | |
"+") | |
end | |
def create_cells | |
width = @width / 9 | |
height = @height / 9 | |
0.upto(8) do |row| | |
0.upto(8) do |column| | |
@cells << Cell.new(@window, | |
@x1 + width * row, | |
@y1 + height * column, | |
row, | |
column, | |
width, | |
height) | |
end | |
end | |
end | |
def in?(x, y) | |
@x1 < x && @x2 > x && | |
@y1 < y && @y2 > y | |
end | |
def in_top_tray?(x, y) | |
@top_tray.in?(x, y) | |
end | |
def in_bottom_tray?(x, y) | |
@bottom_tray.in?(x, y) | |
end | |
def at(x, y) | |
@cells.find {|cell| cell.in?(x, y) } | |
end | |
def top_tray_at(x, y) | |
@top_tray.at(x, y) | |
end | |
def bottom_tray_at(x, y) | |
@bottom_tray.at(x, y) | |
end | |
def to_csa | |
@board.to_csa | |
end | |
def set_from_csa(csa) | |
@board.set_from_csa(csa) | |
end | |
def draw | |
draw_background | |
draw_frame | |
draw_pieces | |
@top_tray.draw_background | |
@bottom_tray.draw_background | |
end | |
def move(before, after) | |
before_csa = before.to_csa | |
piece = before.piece || @board.at(before_csa) | |
return unless piece | |
return if piece == "" | |
after_csa = after.to_csa | |
return if before_csa == after_csa | |
csa_movement = "#{piece[0]}#{before_csa}#{after_csa}#{piece[1..2]}" | |
begin | |
@board.move(csa_movement) | |
rescue Shogi::Board::MoveError, Shogi::Board::FormatError | |
return false | |
end | |
true | |
end | |
def promote_toggle(x, y) | |
before_csa = @board.to_csa | |
lines = before_csa.each_line.to_a | |
@cell = at(x, y) | |
line = lines[@cell.column] | |
cells = line[2..-1].scan(/.{3}/).each.to_a | |
piece = cells[@cell.row] | |
[ | |
["FU", "TO"], | |
["KY", "NY"], | |
["KE", "NK"], | |
["GI", "NG"], | |
["KA", "UM"], | |
["HI", "RY"], | |
].each do |pair| | |
if piece.include?(pair[0]) | |
before = pair[0] | |
after = pair[1] | |
elsif piece.include?(pair[1]) | |
before = pair[1] | |
after = pair[0] | |
else | |
next | |
end | |
header = line[0..1] | |
new_piece = piece.sub(before, after) | |
cells[@cell.row] = new_piece | |
lines[@cell.column] = header + cells.join + "\n" | |
@board.set_from_csa(lines.join) | |
return true | |
end | |
false | |
end | |
def draw_background | |
@window.draw_quad(@x1, @y1, @background_color, | |
@x2, @y1, @background_color, | |
@x2, @y2, @background_color, | |
@x1, @y2, @background_color, | |
0) | |
end | |
def draw_frame | |
width = @width / 9 | |
height = @height / 9 | |
color = Gosu::Color::BLACK | |
0.upto(9) do |row| | |
x1 = @x1 + height * row | |
y1 = @y1 | |
x2 = @x1 + height * row | |
y2 = @y1 + @height | |
@window.draw_line(x1, y1, color, | |
x2, y2, color, | |
1) | |
end | |
0.upto(9) do |column| | |
x1 = @x1 | |
y1 = @y1 + width * column | |
x2 = @x1 + @width | |
y2 = @y1 + width * column | |
@window.draw_line(x1, y1, color, | |
x2, y2, color, | |
1) | |
end | |
end | |
def draw_pieces | |
width = @width / 9 | |
height = @height / 9 | |
@board.to_csa.each_line do |line| | |
next unless line.start_with?("P") | |
if line.start_with?("P-") | |
@top_tray.draw_pieces(line) | |
next | |
elsif line.start_with?("P+") | |
@bottom_tray.draw_pieces(line) | |
next | |
end | |
row = line[1].to_i | |
line[2..-1].scan(/.{3}/).each_with_index do |piece, i| | |
column = i + 1 | |
next if piece == " * " | |
has = piece[0] | |
type = piece[1..2] | |
angle = (has == "+") ? 0 : 180 | |
x = @x1 + (column - 1) * width + width * 0.5 | |
y = @y1 + (row - 1) * height + height * 0.5 | |
image = Piece.const_get(type) | |
image.draw_rot(x, y, 2, | |
angle, 0.5, 0.5, | |
1.0 * width / image.width, | |
1.0 * height / image.height) | |
end | |
end | |
end | |
end | |
class Window < Gosu::Window | |
def initialize(width=640, height=480) | |
super(width, height, false) | |
self.caption = "RbShogi" | |
@board = Board.new(self, | |
width * 0.2, height * 0.1, | |
width * 0.6, height * 0.8) | |
@sync = nil | |
end | |
def update | |
if !@sync.nil? && button_down?(Gosu::MsLeft) | |
@sync = false | |
end | |
end | |
def draw | |
@board.draw | |
end | |
def button_down(id) | |
case id | |
when Gosu::MsLeft | |
x = mouse_x | |
y = mouse_y | |
if @board.in?(x, y) | |
@button_down_cell = @board.at(x, y) | |
elsif @board.in_top_tray?(x, y) | |
@button_down_cell = @board.top_tray_at(x, y) | |
elsif @board.in_bottom_tray?(x, y) | |
@button_down_cell = @board.bottom_tray_at(x, y) | |
end | |
when Gosu::MsRight, Gosu::MsMiddle # TODO: For Gosu 0.8.0.pre2 | |
x = mouse_x | |
y = mouse_y | |
if @board.in?(x, y) | |
@board.promote_toggle(mouse_x, mouse_y) | |
end | |
unless @sync.nil? | |
write_to_socket | |
end | |
end | |
end | |
def button_up(id) | |
case id | |
when Gosu::MsLeft | |
x = mouse_x | |
y = mouse_y | |
if @board.in?(x, y) && @button_down_cell | |
button_up_cell = @board.at(mouse_x, mouse_y) | |
@board.move(@button_down_cell, button_up_cell) | |
end | |
@button_down_cell = nil | |
unless @sync.nil? | |
write_to_socket | |
end | |
when Gosu::KbEscape | |
close | |
end | |
end | |
def needs_cursor? | |
true | |
end | |
def enable_sync | |
@sync = true | |
prepare_socket | |
end | |
private | |
def prepare_socket | |
Thread.start do | |
loop do | |
if @sync | |
begin | |
socket = TCPSocket.open("localhost", 88880) | |
msg = socket.read | |
socket.close | |
unless msg.empty? | |
@board.set_from_csa(msg.gsub("@@@", "\n")) | |
end | |
@sync_ok = true | |
rescue Errno::ECONNREFUSED, Errno::ECONNRESET | |
sleep 3 | |
end | |
end | |
sleep 1 | |
end | |
end | |
end | |
def write_to_socket | |
return unless @sync_ok | |
begin | |
socket = TCPSocket.open("localhost", 88881) | |
socket.write(@board.to_csa.gsub("\n", "@@@")) | |
socket.close | |
rescue Errno::ECONNREFUSED, Errno::ECONNRESET | |
else | |
@sync = true | |
end | |
end | |
end | |
end | |
window = RbShogi::Window.new(800, 600) | |
window.enable_sync | |
window.show | |
__END__ | |
# | |
# Example of Server | |
# | |
require "socket" | |
@data = "" | |
get_server = TCPServer.new(88881) | |
put_server = TCPServer.new(88880) | |
t1 = Thread.start do | |
loop do | |
socket = get_server.accept | |
msg = socket.gets | |
@data = msg | |
socket.close | |
end | |
end | |
t2 = Thread.start do | |
loop do | |
socket = put_server.accept | |
socket.write(@data) | |
socket.close | |
end | |
end | |
t1.join | |
t2.join | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment