eighty-one 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 608d79543717c3d25a2c22590db7e807d153a3b9c3cce49026d40d70dd0a0d00
4
+ data.tar.gz: 3aa116facf776eca0c8b9229c11864fdb5c4731225d86cfc9390fc83dfecdd89
5
+ SHA512:
6
+ metadata.gz: a0885c724c76d86b541d7cc66e1f89ec3fe79fefed1a47268eeca021dee20de5ff8f03841f2aa3e9f8e8930f2b1486c9ac96133d3d290ee28d15bf3fd2633606
7
+ data.tar.gz: 4403cf8c809dfdbb12f5a105a4ed26f7bf30d9041dbab42bfd0b58c082516c742787988a70720079b5e1fa37120ebbdddc42ef0ac99017e0300d715f35618d2f
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in eighty-one.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ eighty-one (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ power_assert (1.1.1)
10
+ rake (10.5.0)
11
+ test-unit (3.2.7)
12
+ power_assert
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ bundler (~> 1.16)
19
+ eighty-one!
20
+ rake (~> 10.0)
21
+ test-unit
22
+
23
+ BUNDLED WITH
24
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 youchan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # EightyOne
2
+
3
+ A game engine of Shogi.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'eighty-one'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install eighty-one
20
+
21
+ ## Contributing
22
+
23
+ Bug reports and pull requests are welcome on GitHub at https://github.com/youchan/eighty-one.
24
+
25
+ ## License
26
+
27
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Run tests"
4
+ task :test do
5
+ ruby("test/run_test.rb")
6
+ end
7
+
8
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "eighty/one"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "eighty_one/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "eighty-one"
8
+ spec.version = EightyOne::VERSION
9
+ spec.authors = ["youchan"]
10
+ spec.email = ["youchan01@gmail.com"]
11
+
12
+ spec.summary = %q{A game engine of Shogi.}
13
+ spec.description = %q{A game engine of Shogi.}
14
+ spec.homepage = "https://github.com/youchan/eighty-one"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.16"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "test-unit"
27
+ end
@@ -0,0 +1,183 @@
1
+ module EightyOne
2
+ class Board
3
+ include Helper
4
+
5
+ def initialize
6
+ @board = Array.new(81)
7
+ @hands = Struct.new(:sente, :gote).new([], [])
8
+ end
9
+
10
+ def initial_state
11
+ (1..9).each do |i|
12
+ self[i, 7] = Pieces::Fu.new(:sente)
13
+ self[i, 3] = Pieces::Fu.new(:gote)
14
+ end
15
+ [1, 9].each do |i|
16
+ self[i, 9] = Pieces::Ky.new(:sente)
17
+ self[i, 1] = Pieces::Ky.new(:gote)
18
+ end
19
+ [2, 8].each do |i|
20
+ self[i, 9] = Pieces::Ke.new(:sente)
21
+ self[i, 1] = Pieces::Ke.new(:gote)
22
+ end
23
+ [3, 7].each do |i|
24
+ self[i, 9] = Pieces::Gi.new(:sente)
25
+ self[i, 1] = Pieces::Gi.new(:gote)
26
+ end
27
+ [4, 6].each do |i|
28
+ self[i, 9] = Pieces::Ki.new(:sente)
29
+ self[i, 1] = Pieces::Ki.new(:gote)
30
+ end
31
+ self[2, 8] = Pieces::Ka.new(:sente)
32
+ self[8, 2] = Pieces::Ka.new(:gote)
33
+ self[8, 8] = Pieces::Hi.new(:sente)
34
+ self[2, 2] = Pieces::Hi.new(:gote)
35
+ self[5, 9] = Pieces::Ou.new(:sente)
36
+ self[5, 1] = Pieces::Ou.new(:gote)
37
+ end
38
+
39
+ def inside?(col, row)
40
+ 1 <= row && 9 >= row && 1 <= col && 9 >= col
41
+ end
42
+
43
+ def double_fu?(piece, col, row)
44
+ piece.face.symbol == :FU && (1..9).any?{|i| self[i, row]&.yield_self{|p| p.face.symbol == :FU && p.turn == piece.turn } }
45
+ end
46
+
47
+ def can_move_to_any_place?(piece, col, row)
48
+ !([:FU, :KY].include?(piece.face.symbol) && (piece.sente? ? col == 1 : col == 9))
49
+ end
50
+
51
+ def placeable?(piece, col, row, from_hand = false)
52
+ res = inside?(col, row) && !(self[col, row]&.turn == piece.turn)
53
+ res &&= can_move_to_any_place?(piece, col, row) && !double_fu?(piece, col, row) if from_hand
54
+ res
55
+ end
56
+
57
+ def []=(col, row, value)
58
+ assert(inside?(col, row))
59
+ assert(value.nil? || Piece === value)
60
+ @board[(row - 1) * 9 + 9 - col] = value
61
+ end
62
+
63
+ def [](col, row)
64
+ @board[(row - 1) * 9 + 9 - col]
65
+ end
66
+
67
+ def sente_hands
68
+ @hands.sente
69
+ end
70
+
71
+ def gote_hands
72
+ @hands.gote
73
+ end
74
+
75
+ alias :at :[]
76
+
77
+ def row(row)
78
+ @board[(row - 1) * 9, 9].reverse
79
+ end
80
+
81
+ def dests_from(col, row)
82
+ piece = self.at(col, row)
83
+ assert(Piece === piece)
84
+ dest = Proc.new{|(c, r)| [col + c, piece.sente? ? row - r : row + r] }
85
+ piece.face.movements.map do |m|
86
+ if EightyOne::Faces::Direction === m
87
+ captured = false
88
+ m.map{|p| dest[p] }.take_while do |p|
89
+ (!captured && placeable?(piece, *p)).tap do |x|
90
+ captured = x && self.at(*p)
91
+ end
92
+ end
93
+ else
94
+ dest[m].yield_self{|p| placeable?(piece, *p) ? [p] : [] }
95
+ end
96
+ end.flatten(1)
97
+ end
98
+
99
+ def move_from(col, row)
100
+ Movement.new(self, col, row)
101
+ end
102
+
103
+ def capture(piece)
104
+ @hands[piece.turn] << piece
105
+ end
106
+
107
+ def place(piece, col, row)
108
+ dest = self.at(col, row)
109
+ if dest
110
+ assert(Piece === dest)
111
+ assert(dest.turn != piece.turn)
112
+ capture(dest.reset(piece.turn))
113
+ end
114
+ self[col, row] = piece
115
+ end
116
+
117
+ def encode
118
+ board_map = (0..81).step(4).map do |i|
119
+ @board.slice(i, 4).map.with_index{|piece, i| (piece ? 1 : 0) << (3 - i) }.sum.to_s(16)
120
+ end.join
121
+
122
+ on_board = (1..9).map do |row|
123
+ row(row).compact.map(&:to_s).join
124
+ end.join
125
+
126
+ hands = @hands.sente.map(&:to_s).join + @hands.gote.map(&:to_s).join
127
+
128
+ board_map + on_board + ?/ + hands
129
+ end
130
+
131
+ def self.decode(code)
132
+ board = Board.new
133
+ (board_map, pieces) = code.unpack('a21a*')
134
+ (on_board, hands) = pieces.split(?/).map{|x| x.chars.each_slice(3).map(&:join) }
135
+
136
+ code[0,21].to_i(16).to_s(2).chars.each_slice(9).with_index do |row, y|
137
+ row.each_with_index do |cell, x|
138
+ board[9 - x, y + 1] = Piece.decode(on_board.shift) if cell == '1'
139
+ end
140
+ end
141
+
142
+ hands.map{|s| Piece.decode(s) }.each do |piece|
143
+ case piece.turn
144
+ when :sente
145
+ board.sente_hands << piece
146
+ when :gote
147
+ board.gote_hands << piece
148
+ end
149
+ end
150
+ board
151
+ end
152
+
153
+ def to_csi
154
+ (1..9).map do |i|
155
+ "P#{i}" + row(i).map{|c| c ? c.to_s : ' * '}.join
156
+ end.join(?\n)
157
+ end
158
+
159
+ def to_s
160
+ self.to_csi
161
+ end
162
+
163
+ class Movement
164
+ include Helper
165
+
166
+ def initialize(board, col, row)
167
+ @from = [col, row]
168
+ @board = board
169
+ @piece = board.at(col, row)
170
+ assert(Piece === @piece)
171
+ end
172
+
173
+ def to(col, row)
174
+ if @board.dests_from(*@from).include?([col, row])
175
+ @board[*@from] = nil
176
+ @board.place(@piece, col, row)
177
+ else
178
+ raise CantGetMovement.new
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,49 @@
1
+ module EightyOne
2
+ class Face
3
+ attr_reader :symbol, :movements
4
+
5
+ def initialize(symbol, movements)
6
+ @symbol = symbol
7
+ @movements = movements
8
+ end
9
+ end
10
+
11
+ module Faces
12
+ class Direction
13
+ include Enumerable
14
+
15
+ attr_reader :dir
16
+
17
+ def initialize(col, row)
18
+ @dir = [col, row]
19
+ end
20
+
21
+ def each
22
+ (1..8).each do |i|
23
+ yield [@dir[0] * i, @dir[1] * i]
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.dir(col, row)
29
+ Direction.new(col, row)
30
+ end
31
+
32
+ FU = Face.new(:FU, [[0, 1]])
33
+ KY = Face.new(:KY, [dir(0, 1)])
34
+ KE = Face.new(:KE, [[-1, 2], [1, 2]])
35
+ GI = Face.new(:GI, [[-1, 1], [0, 1], [1, 1], [-1, -1], [1, -1]])
36
+ KI = Face.new(:KI, [[-1, 1], [0, 1], [1, 1], [-1, 0], [-1, 0], [0, -1]])
37
+ KA = Face.new(:KA, [dir(-1, 1), dir(1, 1), dir(-1, -1), dir(1, -1)])
38
+ HI = Face.new(:HI, [dir(0, 1), dir(0, -1), dir(-1, 0), dir(1, 0)])
39
+ OU = Face.new(:OU, [[-1, 1], [0, 1], [1, 1], [-1, 0], [1, 0], [-1, -1], [0, -1], [1, -1]])
40
+ TO = Face.new(:TO, KI.movements)
41
+ NY = Face.new(:NY, KI.movements)
42
+ NK = Face.new(:NK, KI.movements)
43
+ NG = Face.new(:NG, KI.movements)
44
+ UM = Face.new(:UM, KA.movements + [[0, 1], [-1, 0], [1, 0], [0, -1]])
45
+ RY = Face.new(:RY, HI.movements + [[-1, 1], [1, 1], [-1, -1], [1, -1]])
46
+
47
+ ALL = [ FU, KY, KE, GI, KI, KA, HI, OU, TO, NY, NK, NG, UM, RY ]
48
+ end
49
+ end
@@ -0,0 +1,94 @@
1
+ module EightyOne
2
+ class Piece
3
+ include Helper
4
+
5
+ def self.new_class(forward, backword)
6
+ Class.new(self) do |c|
7
+ c.instance_eval do
8
+ define_method(:forward) { forward }
9
+ define_singleton_method(:forward) { forward }
10
+ define_method(:backward) { backward }
11
+ define_singleton_method(:backward) { backward }
12
+ end
13
+ end
14
+ end
15
+
16
+ attr_reader :turn
17
+
18
+ def initialize(turn)
19
+ assert(turn == :sente || turn == :gote)
20
+ @turn = turn
21
+ @promoted = false
22
+ end
23
+
24
+ def face
25
+ @promoted ? backward : forward
26
+ end
27
+
28
+ def reset(turn)
29
+ @turn = turn
30
+ @promote = false
31
+ self
32
+ end
33
+
34
+ def promote
35
+ @promoted = true
36
+ self
37
+ end
38
+
39
+ def promoted?
40
+ @promoted
41
+ end
42
+
43
+ def forward?
44
+ !@promoted
45
+ end
46
+
47
+ def opposite
48
+ @turn.sente? ? :gote : :sente
49
+ end
50
+
51
+ def sente?
52
+ @turn == :sente
53
+ end
54
+
55
+ def gote?
56
+ @turn == :gote
57
+ end
58
+
59
+ def encode
60
+ (@turn == :sente ? ?+ : ?-) + face.symbol.to_s
61
+ end
62
+
63
+ def to_s
64
+ self.encode
65
+ end
66
+
67
+ def self.decode(code)
68
+ turn = code[0] == ?+ ? :sente : :gote
69
+ symbol = code[1, 2].to_sym
70
+ piece_class = Pieces::ALL.find{|p| p.forward.symbol == symbol }
71
+ if piece_class
72
+ piece_class.new(turn)
73
+ else
74
+ piece_class = ALL.find{|p| p.backword.symbol == symbol }
75
+ if piece_class
76
+ piece_class.new(turn).promote
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ module Pieces
83
+ Fu = Piece.new_class(Faces::FU, Faces::TO)
84
+ Ky = Piece.new_class(Faces::KY, Faces::NY)
85
+ Ke = Piece.new_class(Faces::KE, Faces::NK)
86
+ Gi = Piece.new_class(Faces::GI, Faces::NG)
87
+ Ki = Piece.new_class(Faces::KI, nil)
88
+ Ka = Piece.new_class(Faces::KA, Faces::UM)
89
+ Hi = Piece.new_class(Faces::HI, Faces::RY)
90
+ Ou = Piece.new_class(Faces::OU, nil)
91
+
92
+ ALL = [Fu, Ky, Ke, Gi, Ki, Ka, Hi, Ou]
93
+ end
94
+ end
@@ -0,0 +1,3 @@
1
+ module EightyOne
2
+ VERSION = "0.1.0"
3
+ end
data/lib/eighty_one.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "eighty_one/version"
2
+
3
+ module EightyOne
4
+ class CantGetMovement < RuntimeError; end
5
+ module Helper
6
+ def assert(*conds)
7
+ unless conds.all?
8
+ e = RuntimeError.new 'assert error'
9
+ e.set_backtrace(caller)
10
+ raise e
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ require 'eighty_one/face'
17
+ require 'eighty_one/piece'
18
+ require 'eighty_one/board'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eighty-one
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - youchan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A game engine of Shogi.
56
+ email:
57
+ - youchan01@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - eighty-one.gemspec
71
+ - lib/eighty_one.rb
72
+ - lib/eighty_one/board.rb
73
+ - lib/eighty_one/face.rb
74
+ - lib/eighty_one/piece.rb
75
+ - lib/eighty_one/version.rb
76
+ homepage: https://github.com/youchan/eighty-one
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.7.3
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A game engine of Shogi.
100
+ test_files: []