Skip to content

Instantly share code, notes, and snippets.

@myokoym
Last active August 29, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myokoym/10743703 to your computer and use it in GitHub Desktop.
Save myokoym/10743703 to your computer and use it in GitHub Desktop.
A Shogi board using Gosu and Ruby. (under construction...)
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