reversi 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dde2e7d7a4f73ef89fbdc626ff5f06d22e0451fa
4
- data.tar.gz: d3d2a7f4eea6769ea366c8cd3a1a059238cbf0f4
3
+ metadata.gz: 651aae750b3b767bc9375470adff0bb5414f0663
4
+ data.tar.gz: b90881770c721b08e945d7f20b3433bf97437115
5
5
  SHA512:
6
- metadata.gz: 96c2d82cfb35399f3951b62c08a18d024b564a146d588311ce3ef1790d512be2d9d77e1def514be5fd23dae97509a979cfb5308c9b03a2e92d55365a5375fefa
7
- data.tar.gz: 32d24821205506520d37fec291679fa21ca5e30369bdafa1ed5f7a8772008f9b207189864ce286f9a2dd16b23030b2af11ddafa4d680ec7287bd6580e4536ba2
6
+ metadata.gz: 85398b003393d63e49e37bbdf9e59958d2e3cdf49d9d787b4bd95dfd1261d2498b7b1f217a21909ddd283ab59f12893d5f909294dda5887bf5d5ba375ec5067e
7
+ data.tar.gz: 81821d25c301e6e18a0bda8088a730558ec96a00e0317eb104e03fbd83f2de2b8b4601d7bd5a0629c254d52c583b493cc21b8b57bca6f36c916b8a5a8056952c
data/README.md CHANGED
@@ -47,18 +47,21 @@ Use `Reversi.configure` to configure setting for a reversi game.
47
47
 
48
48
  `name` description... (default value)
49
49
 
50
- * `player_b` A player having the first move uses this class object. (Reversi::Player::RandomAI)
51
- * `player_w` A player having the passive move uses this class object. (Reversi::Player::RandomAI)
52
- * `disk_b` A string of the black disks. ('b')
53
- * `disk_w` A string of the black disks. ('w')
54
- * `disk_color_b` A color of the black disks. (0)
55
- * `disk_color_w` A color of the black disks. (0)
56
- * `progress` Whether or not the progress of the game is displayed. (false)
57
- * `stack_limit` The upper limit number of times of use `Reversi::Board#undo!` . (3)
50
+ * `player_b` A player having the first move uses this class object. ( Reversi::Player::RandomAI )
51
+ * `player_w` A player having the passive move uses this class object. ( Reversi::Player::RandomAI )
52
+ * `disk_b` A string of the black disks. ( 'b' )
53
+ * `disk_w` A string of the black disks. ( 'w' )
54
+ * `disk_color_b` A color of the black disks. ( 0 )
55
+ * `disk_color_w` A color of the black disks. ( 0 )
56
+ * `initial_position` The initial positions of each disk on the board. ( {:black => [[:d, 5], [:e, 4]], :white => [[:d, 4], [:e, 5]]} )
57
+ * `progress` Whether or not the progress of the game is displayed. ( false )
58
+ * `stack_limit` The upper limit number of times of use `Reversi::Board#undo!` . ( 3 )
58
59
 
59
60
  A string and a color of the disks are reflected on `game.board.to_s` .
60
61
  You can choose from 9 colors, black, red, green, yellow, blue, magenda, cyan, white and gray.
61
62
 
63
+ Using `Reversi.reset` method, you can reset all options to the default values.
64
+
62
65
  ### Human vs Computer
63
66
 
64
67
  Set `Reversi::Player::Human` to player_b or player_w, and run. Please input your move (for example: d3). This program is terminated when this game is over or when you input `q` or `exit`.
@@ -72,7 +75,7 @@ game = Reversi::Game.new
72
75
  game.start
73
76
  ```
74
77
 
75
- ### My AI
78
+ ### Your Original Player
76
79
 
77
80
  You can make your original player class by inheriting `Reversi::Player::BasePlayer` and defining `move` method.
78
81
 
data/lib/reversi/board.rb CHANGED
@@ -32,13 +32,14 @@ module Reversi
32
32
  @options = options
33
33
  @stack = []
34
34
  [:disk_color_b, :disk_color_w].each do |color|
35
- if options[color].is_a?(Symbol) || options[color].is_a?(String)
36
- options[color] = DISK_COLOR[options[color].to_sym].to_i
35
+ if @options[color].is_a?(Symbol) || @options[color].is_a?(String)
36
+ @options[color] = DISK_COLOR[@options[color].to_sym].to_i
37
37
  end
38
38
  end
39
39
  @columns = (0..9).map{ (0..9).map{ |_| DISK[:none] } }
40
- put_disk(4, 4, :white); put_disk(5, 5, :white)
41
- put_disk(4, 5, :black); put_disk(5, 4, :black)
40
+ @options[:initial_position].each do |color, positions|
41
+ positions.each{ |position| put_disk(*position, color) }
42
+ end
42
43
  @columns.each do |col|
43
44
  col[0] = 2; col[-1] = 2
44
45
  end.tap do |cols|
@@ -50,7 +51,7 @@ module Reversi
50
51
  #
51
52
  # @return [String]
52
53
  def to_s
53
- " #{(:a..:h).to_a.map(&:to_s).join(" ")}\n" <<
54
+ " #{[*'a'..'h'].join(" ")}\n" <<
54
55
  " #{"+---"*8}+\n" <<
55
56
  @columns[1][1..-2].zip(*@columns[2..8].map{ |col| col[1..-2] })
56
57
  .map{ |row| row.map do |e|
@@ -61,7 +62,7 @@ module Reversi
61
62
  end
62
63
  end
63
64
  .map{ |e| "| #{e} |" }.join }.map{ |e| e.gsub(/\|\|/, "|") }
64
- .tap{ |rows| break (0..7).to_a.map{ |i| " #{i+1} " << rows[i] } }
65
+ .tap{ |rows| break [*0..7].map{ |i| " #{i+1} " << rows[i] } }
65
66
  .join("\n #{"+---"*8}+\n") <<
66
67
  "\n #{"+---"*8}+\n"
67
68
  end
@@ -69,6 +70,7 @@ module Reversi
69
70
  # Pushes an array of the game board onto a stack.
70
71
  # The stack size limit is 3(default).
71
72
  def push_stack
73
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
72
74
  @stack.push(Marshal.load(Marshal.dump(@columns)))
73
75
  @stack.shift if @stack.size > @options[:stack_limit]
74
76
  end
@@ -96,7 +98,7 @@ module Reversi
96
98
  # @param y [Integer] the row number
97
99
  # @return [Integer] the openness
98
100
  def openness(x, y)
99
- x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
101
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
100
102
  ([-1,0,1].product([-1,0,1]) - [[0, 0]]).inject(0) do |sum, (dx, dy)|
101
103
  sum + (@columns[x + dx][y + dy] == 0 ? 1 : 0)
102
104
  end
@@ -108,7 +110,7 @@ module Reversi
108
110
  # @param y [Integer] the row number
109
111
  # @return [Symbol] the color or `:none`
110
112
  def at(x, y)
111
- x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
113
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
112
114
  DISK.key(@columns[x][y])
113
115
  end
114
116
 
@@ -139,7 +141,7 @@ module Reversi
139
141
  # @param y [Integer] the row number
140
142
  # @param color [Symbol]
141
143
  def put_disk(x, y, color)
142
- x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
144
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
143
145
  @columns[x][y] = DISK[color.to_sym]
144
146
  end
145
147
 
@@ -150,10 +152,9 @@ module Reversi
150
152
  # @param y [Integer] the row number
151
153
  # @param color [Symbol]
152
154
  def flip_disks(x, y, color)
153
- x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
155
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
154
156
  [-1,0,1].product([-1,0,1]).each do |dx, dy|
155
157
  next if dx == 0 && dy == 0
156
- # 隣接石が異色であったらflippable?でひっくり返せるか(挟まれているか)確認
157
158
  if @columns[x + dx][y + dy] == DISK[color]*(-1)
158
159
  flip_disk(x, y, dx, dy, color) if flippable?(x, y, dx, dy, color)
159
160
  end
@@ -184,7 +185,7 @@ module Reversi
184
185
  # @param color [Symbol]
185
186
  # @return [Boolean]
186
187
  def puttable?(x, y, color)
187
- x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
188
+ x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
188
189
  return false if @columns[x][y] != 0
189
190
  [-1,0,1].product([-1,0,1]).each do |dx, dy|
190
191
  next if dx == 0 && dy == 0
@@ -8,10 +8,23 @@ module Reversi
8
8
  :disk_w,
9
9
  :disk_color_b,
10
10
  :disk_color_w,
11
+ :initial_position,
11
12
  :progress,
12
13
  :stack_limit
13
14
  ].freeze
14
15
 
16
+ DEFAULTS = {
17
+ :player_b => Reversi::Player::RandomAI,
18
+ :player_w => Reversi::Player::RandomAI,
19
+ :disk_b => 'b',
20
+ :disk_w => 'w',
21
+ :disk_color_b => 0,
22
+ :disk_color_w => 0,
23
+ :initial_position => {:black => [[:d, 5], [:e, 4]], :white => [[:d, 4], [:e, 5]]},
24
+ :progress => false,
25
+ :stack_limit => 3
26
+ }
27
+
15
28
  attr_accessor *OPTIONS_KEYS
16
29
 
17
30
  def configure
@@ -22,15 +35,18 @@ module Reversi
22
35
  Hash[*OPTIONS_KEYS.map{|key| [key, send(key)]}.flatten]
23
36
  end
24
37
 
38
+ def reset
39
+ DEFAULTS.each do |option, default|
40
+ self.send("#{option}=".to_sym, default)
41
+ end
42
+ end
43
+
25
44
  def set_defaults
26
- self.player_b ||= Reversi::Player::RandomAI
27
- self.player_w ||= Reversi::Player::RandomAI
28
- self.disk_b ||= "b"
29
- self.disk_w ||= "w"
30
- self.disk_color_b ||= 0
31
- self.disk_color_w ||= 0
32
- self.progress ||= false
33
- self.stack_limit ||= 3
45
+ DEFAULTS.each do |option, default|
46
+ default.class == String ?
47
+ eval("self.#{option} ||= \'#{default}\'") :
48
+ eval("self.#{option} ||= #{default}")
49
+ end
34
50
  end
35
51
  end
36
52
  end
@@ -23,10 +23,7 @@ module Reversi::Player
23
23
  @board.push_stack
24
24
  color = my_color ? @my_color : @opponent_color
25
25
  x = (:a..:h).to_a.index(x) + 1 if x.is_a? Symbol
26
- diff = flip_disks(x, y, color)
27
- if diff.empty?
28
- raise Reversi::MoveError, "A player must flip at least one or more opponent's disks."
29
- end
26
+ flip_disks(x, y, color)
30
27
  @board.put_disk(x, y, color)
31
28
  end
32
29
 
@@ -1,5 +1,5 @@
1
1
  module Reversi::Player
2
- class NegamaxAI < Reversi::Player::BasePlayer
2
+ class NegamaxAI < BasePlayer
3
3
 
4
4
  POINT = {
5
5
  [:a, 1] => 100, [:b, 1] => -10, [:c, 1] => 0, [:d, 1] => -1, [:e, 1] => -1, [:f, 1] => 0, [:g, 1] => -10, [:h, 1] => 100,
@@ -1,3 +1,3 @@
1
1
  module Reversi
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -12,12 +12,6 @@ describe Reversi::Player::BasePlayer do
12
12
  end
13
13
  end
14
14
 
15
- context "when a player make an invalid move" do
16
- it "Reversi::MoveError raised" do
17
- expect{ player.put_disk(:a, 1) }.to raise_error Reversi::MoveError
18
- end
19
- end
20
-
21
15
  context "when the third argument is `false`" do
22
16
  it "makes a opponent's move" do
23
17
  expect{ player.put_disk(:e, 3, false) }.to change{ board.status[:white].size }.by(2)
data/spec/board_spec.rb CHANGED
@@ -8,6 +8,8 @@ describe Reversi::Board do
8
8
  :disk_w => "w",
9
9
  :disk_color_b => disk_color_b,
10
10
  :disk_color_w => disk_color_w,
11
+ :initial_position => {:black => [[:d, 5], [:e, 4]],
12
+ :white => [[:d, 4], [:e, 5]]},
11
13
  :progress => false,
12
14
  :stack_limit => 3}
13
15
  end
@@ -33,6 +35,35 @@ describe Reversi::Board do
33
35
  end
34
36
  end
35
37
 
38
+ describe "#to_s" do
39
+ let(:disk_color_b) { :black }
40
+ let(:disk_color_w) { 'white' }
41
+
42
+ it do
43
+ str = <<-END.gsub(/ {6}/,"")
44
+ a b c d e f g h
45
+ +---+---+---+---+---+---+---+---+
46
+ 1 | | | | | | | | |
47
+ +---+---+---+---+---+---+---+---+
48
+ 2 | | | | | | | | |
49
+ +---+---+---+---+---+---+---+---+
50
+ 3 | | | | | | | | |
51
+ +---+---+---+---+---+---+---+---+
52
+ 4 | | | | \e[37mw\e[0m | \e[30mb\e[0m | | | |
53
+ +---+---+---+---+---+---+---+---+
54
+ 5 | | | | \e[30mb\e[0m | \e[37mw\e[0m | | | |
55
+ +---+---+---+---+---+---+---+---+
56
+ 6 | | | | | | | | |
57
+ +---+---+---+---+---+---+---+---+
58
+ 7 | | | | | | | | |
59
+ +---+---+---+---+---+---+---+---+
60
+ 8 | | | | | | | | |
61
+ +---+---+---+---+---+---+---+---+
62
+ END
63
+ expect(board.to_s).to eq str
64
+ end
65
+ end
66
+
36
67
  describe "#push_stack" do
37
68
  let(:disk_color_b) { 0 }
38
69
  let(:disk_color_w) { 0 }
data/spec/game_spec.rb CHANGED
@@ -34,6 +34,35 @@ describe Reversi::Game do
34
34
  end
35
35
  end
36
36
 
37
+ describe "initial position" do
38
+ it "default" do
39
+ game = Reversi::Game.new
40
+ expect(game.board.at(:e, 4)).to eq :black
41
+ expect(game.board.at(:d, 4)).to eq :white
42
+ end
43
+
44
+ it "original position" do
45
+ initial = {:black => [[:a, 1]], :white => [[:h, 8]]}
46
+ options = {:initial_position => initial}
47
+ game = Reversi::Game.new(options)
48
+ expect(game.board.at(:d, 4)).to eq :none
49
+ expect(game.board.at(:a, 1)).to eq :black
50
+ expect(game.board.at(:h, 8)).to eq :white
51
+ end
52
+ end
53
+
54
+ describe "Reversi.reset" do
55
+ it "reset all options to the default values" do
56
+ Reversi.configure{ |config| config.progress = true }
57
+ game = Reversi::Game.new
58
+ expect(game.options[:progress]).to eq true
59
+
60
+ Reversi.reset
61
+ game = Reversi::Game.new
62
+ expect(game.options[:progress]).to eq false
63
+ end
64
+ end
65
+
37
66
  describe "from a record of a reversi game" do
38
67
  game = Reversi::Game.new
39
68
  game.player_b.put_disk(:c, 4); game.player_w.put_disk(:e, 3)
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ class ValidPlayer < Reversi::Player::BasePlayer
4
+ def move(board)
5
+ moves = next_moves.map{ |v| v[:move] }
6
+ put_disk(*moves.sample) unless moves.empty?
7
+ end
8
+ end
9
+
10
+ class InvalidPlayer1 < Reversi::Player::BasePlayer
11
+ def move(board)
12
+ put_disk(:a, 1)
13
+ end
14
+ end
15
+
16
+ class InvalidPlayer2 < Reversi::Player::BasePlayer
17
+ def move(board)
18
+ put_disk(:d, 4)
19
+ end
20
+ end
21
+
22
+ class InvalidPlayer3 < Reversi::Player::BasePlayer
23
+ def move(board)
24
+ put_disk(:a, 1)
25
+ put_disk(:a, 2)
26
+ end
27
+ end
28
+
29
+ describe Reversi::Player do
30
+ let(:game) { Reversi::Game.new }
31
+
32
+ describe ValidPlayer do
33
+ it "exit successfully" do
34
+ Reversi.configure do |config|
35
+ config.player_b = ValidPlayer
36
+ end
37
+ expect(game.start).to eq nil
38
+ end
39
+ end
40
+
41
+ describe InvalidPlayer1 do
42
+ it "should raise Reversi::MoveError" do
43
+ Reversi.configure do |config|
44
+ config.player_b = InvalidPlayer1
45
+ end
46
+ message = "A player must flip at least one or more opponent's disks."
47
+ expect{ game.start }.to raise_error(Reversi::MoveError, message)
48
+ end
49
+ end
50
+
51
+ describe InvalidPlayer2 do
52
+ it "should raise Reversi::MoveError" do
53
+ Reversi.configure do |config|
54
+ config.player_b = InvalidPlayer2
55
+ end
56
+ message = "When a player can't make a valid move, you must not place a new disk."
57
+ expect{ game.start }.to raise_error(Reversi::MoveError, message)
58
+ end
59
+ end
60
+
61
+ describe InvalidPlayer3 do
62
+ it "should raise Reversi::MoveError" do
63
+ Reversi.configure do |config|
64
+ config.player_b = InvalidPlayer3
65
+ end
66
+ message = "A player must place a new disk on the board."
67
+ expect{ game.start }.to raise_error(Reversi::MoveError, message)
68
+ end
69
+ end
70
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reversi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - seinosuke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-04 00:00:00.000000000 Z
11
+ date: 2015-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -54,6 +54,7 @@ files:
54
54
  - spec/base_player_spec.rb
55
55
  - spec/board_spec.rb
56
56
  - spec/game_spec.rb
57
+ - spec/player_spec.rb
57
58
  - spec/spec_helper.rb
58
59
  homepage: https://github.com/seinosuke/reversi
59
60
  licenses:
@@ -83,5 +84,6 @@ test_files:
83
84
  - spec/base_player_spec.rb
84
85
  - spec/board_spec.rb
85
86
  - spec/game_spec.rb
87
+ - spec/player_spec.rb
86
88
  - spec/spec_helper.rb
87
89
  has_rdoc: