linotype 0.0.3 → 0.0.4
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.
- data/lib/linotype/game.rb +39 -18
- data/lib/linotype/move.rb +28 -7
- data/lib/linotype/player.rb +9 -0
- data/lib/linotype/simulator.rb +29 -0
- data/lib/linotype/strategy.rb +31 -0
- data/lib/linotype/tile.rb +12 -0
- data/lib/linotype/version.rb +1 -1
- data/lib/linotype.rb +2 -3
- metadata +4 -2
data/lib/linotype/game.rb
CHANGED
@@ -2,11 +2,12 @@ module Linotype
|
|
2
2
|
class Game
|
3
3
|
|
4
4
|
attr_accessor :moves
|
5
|
-
attr_reader :current_player
|
5
|
+
attr_reader :current_player, :players
|
6
6
|
|
7
7
|
def initialize(args={})
|
8
|
+
args = { player_one: Player.new, player_two: Player.new }.merge(args)
|
8
9
|
@board = Board.new(self, tiles: create_tile_letter_array(args[:tiles]))
|
9
|
-
@players = [
|
10
|
+
@players = [args[:player_one], args[:player_two]]
|
10
11
|
@current_player = @players[0]
|
11
12
|
@moves = []
|
12
13
|
end
|
@@ -27,7 +28,7 @@ module Linotype
|
|
27
28
|
letter_array
|
28
29
|
end
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
def play(*tile_coordinates)
|
32
33
|
tiles = find_tiles(tile_coordinates)
|
33
34
|
Move.new(self, @current_player, tiles).valid?
|
@@ -64,28 +65,22 @@ module Linotype
|
|
64
65
|
every_play_for_word(word_to_test).each do |tiles|
|
65
66
|
move = Move.new(self, @current_player, tiles)
|
66
67
|
potential_moves << move
|
67
|
-
|
68
|
+
move.undo!
|
68
69
|
end
|
69
70
|
end
|
70
|
-
potential_moves
|
71
|
+
potential_moves
|
71
72
|
end
|
72
73
|
|
73
74
|
def valid_potential_plays
|
74
|
-
test_potential_plays.select { |potential_play| potential_play
|
75
|
+
test_potential_plays.select { |potential_play| potential_play.valid? }
|
75
76
|
end
|
76
77
|
|
77
78
|
def best_next_play
|
78
|
-
valid_potential_plays.sort
|
79
|
+
valid_potential_plays.sort do |a, b|
|
80
|
+
current_player.strategy.score(b) <=> current_player.strategy.score(a)
|
81
|
+
end.first
|
79
82
|
end
|
80
83
|
|
81
|
-
def undo_last_move!
|
82
|
-
if (last_move = moves.pop) && last_move.valid?
|
83
|
-
last_move.uncover_tiles!
|
84
|
-
@current_player = other_player if last_move.valid?
|
85
|
-
end
|
86
|
-
moves.last.cover_tiles! if moves.any?
|
87
|
-
end
|
88
|
-
|
89
84
|
def over?
|
90
85
|
uncovered_tiles.empty? || two_passes_in_a_row?
|
91
86
|
end
|
@@ -148,6 +143,10 @@ module Linotype
|
|
148
143
|
def tile_rows
|
149
144
|
@board.tiles
|
150
145
|
end
|
146
|
+
|
147
|
+
def all_tiles
|
148
|
+
tile_rows.flatten
|
149
|
+
end
|
151
150
|
|
152
151
|
def letters
|
153
152
|
@board.tiles.flatten.collect(&:letter)
|
@@ -172,16 +171,24 @@ module Linotype
|
|
172
171
|
end
|
173
172
|
|
174
173
|
def uncovered_tiles
|
175
|
-
|
174
|
+
all_tiles.select { |tile| !tile.covered_by }
|
176
175
|
end
|
177
176
|
private :uncovered_tiles
|
178
177
|
|
179
178
|
def defended_tiles(player)
|
180
|
-
tile_rows.flatten.select { |tile| tile.covered_by == player && tile.defended? }
|
179
|
+
tile_rows.flatten.select { |tile| player && tile.covered_by == player && tile.defended? }
|
181
180
|
end
|
182
181
|
|
183
182
|
def covered_tiles(player)
|
184
|
-
tile_rows.flatten.select { |tile| tile.covered_by == player }
|
183
|
+
tile_rows.flatten.select { |tile| player && tile.covered_by == player }
|
184
|
+
end
|
185
|
+
|
186
|
+
def edge_tiles(player)
|
187
|
+
tile_rows.flatten.select { |tile| player && tile.edge? && tile.covered_by == player }
|
188
|
+
end
|
189
|
+
|
190
|
+
def corner_tiles(player)
|
191
|
+
tile_rows.flatten.select { |tile| player && tile.corner? && tile.covered_by == player }
|
185
192
|
end
|
186
193
|
|
187
194
|
def two_passes_in_a_row?
|
@@ -189,5 +196,19 @@ module Linotype
|
|
189
196
|
end
|
190
197
|
private :two_passes_in_a_row?
|
191
198
|
|
199
|
+
def print_board
|
200
|
+
tile_rows.each do |row|
|
201
|
+
r = ""
|
202
|
+
row.each do |tile|
|
203
|
+
r << "#{tile.letter}#{tile.covered_by ? player_number(tile.covered_by) : ' '} "
|
204
|
+
end
|
205
|
+
puts r
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def print_scores
|
210
|
+
players.each { |player| puts "Player #{player_number(player)}: #{covered_tiles(player).count}" }
|
211
|
+
end
|
212
|
+
|
192
213
|
end
|
193
214
|
end
|
data/lib/linotype/move.rb
CHANGED
@@ -1,35 +1,56 @@
|
|
1
1
|
module Linotype
|
2
2
|
class Move
|
3
3
|
|
4
|
-
attr_reader :game, :
|
4
|
+
attr_reader :game, :invalid_reason, :score, :tiles
|
5
|
+
attr_accessor :player, :previous_tile_values
|
5
6
|
|
6
7
|
def initialize(game, player, tiles)
|
7
8
|
@game = game
|
8
|
-
|
9
|
+
self.player = player
|
9
10
|
@tiles = tiles
|
11
|
+
set_previous_tile_values
|
10
12
|
calculate_valid
|
11
13
|
if valid?
|
12
14
|
calculate_scores(:before)
|
15
|
+
@game.moves << self
|
13
16
|
cover_tiles!
|
14
17
|
calculate_scores(:after)
|
15
18
|
calculate_net_scores
|
16
19
|
@game.toggle_current_player
|
20
|
+
else
|
21
|
+
@game.moves << self
|
17
22
|
end
|
18
|
-
@game.moves << self
|
19
23
|
end
|
20
24
|
|
21
25
|
def score
|
22
26
|
@score ||= {}
|
23
27
|
end
|
28
|
+
|
29
|
+
def undo!
|
30
|
+
tiles.each { |tile| tile.covered_by = previous_tile_values[tile][:covered_by] }
|
31
|
+
game.moves.delete(self)
|
32
|
+
game.toggle_current_player if valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_previous_tile_values
|
36
|
+
self.previous_tile_values = {}
|
37
|
+
tiles.each do |tile|
|
38
|
+
self.previous_tile_values[tile] = { covered_by: tile.covered_by }
|
39
|
+
end
|
40
|
+
end
|
24
41
|
|
25
42
|
def calculate_scores(time)
|
26
|
-
score["defended_#{time}".to_sym] = @game.defended_tiles(player).count
|
27
|
-
score["covered_#{time}".to_sym] = @game.covered_tiles(player).count
|
43
|
+
self.score["defended_#{time}".to_sym] = @game.defended_tiles(player).count
|
44
|
+
self.score["covered_#{time}".to_sym] = @game.covered_tiles(player).count
|
45
|
+
self.score["edges_#{time}".to_sym] = @game.edge_tiles(player).count
|
46
|
+
self.score["corners_#{time}".to_sym] = @game.corner_tiles(player).count
|
28
47
|
end
|
29
48
|
|
30
49
|
def calculate_net_scores
|
31
|
-
score[:defended] = score[:defended_after] - score[:defended_before]
|
32
|
-
score[:covered] = score[:covered_after] - score[:covered_before]
|
50
|
+
self.score[:defended] = score[:defended_after] - score[:defended_before]
|
51
|
+
self.score[:covered] = score[:covered_after] - score[:covered_before]
|
52
|
+
self.score[:edges] = score[:edges_after] - score[:edges_before]
|
53
|
+
self.score[:corners] = score[:corners_after] - score[:corners_before]
|
33
54
|
end
|
34
55
|
|
35
56
|
def valid?
|
data/lib/linotype/player.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
module Linotype
|
2
2
|
class Player
|
3
3
|
|
4
|
+
DEFAULT_STRATEGY = ->(move) { (move.score[:defended] * 2) + move.score[:covered] }
|
5
|
+
|
6
|
+
attr_accessor :strategy
|
7
|
+
|
8
|
+
def initialize(args={})
|
9
|
+
args = { strategy: DEFAULT_STRATEGY }.merge(args)
|
10
|
+
self.strategy = args[:strategy]
|
11
|
+
end
|
12
|
+
|
4
13
|
end
|
5
14
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Linotype
|
2
|
+
class Simulator
|
3
|
+
|
4
|
+
attr_accessor :player_one, :player_two, :game
|
5
|
+
|
6
|
+
def initialize(strategy_one, strategy_two)
|
7
|
+
self.player_one = Player.new(strategy: strategy_one)
|
8
|
+
self.player_two = Player.new(strategy: strategy_two)
|
9
|
+
end
|
10
|
+
|
11
|
+
def simulate!
|
12
|
+
self.game = Game.new(player_one: player_one, player_two: player_two)
|
13
|
+
puts "Let's start the simulator"
|
14
|
+
while !game.over?
|
15
|
+
if best_next_play = game.best_next_play
|
16
|
+
puts "Player #{game.player_number(game.current_player)} will play: #{best_next_play.word}"
|
17
|
+
game.play(*best_next_play.to_hash[:coordinates])
|
18
|
+
else
|
19
|
+
puts "Player #{game.player_number(game.current_player)} will pass."
|
20
|
+
game.play
|
21
|
+
end
|
22
|
+
game.print_board
|
23
|
+
game.print_scores
|
24
|
+
end
|
25
|
+
game
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Linotype
|
2
|
+
class Strategy
|
3
|
+
|
4
|
+
LETTERPRESS_FAN_HEURISTIC = ->(move) do
|
5
|
+
move.score[:covered] + move.score[:defended] + move.score[:edges] + move.score[:corners]
|
6
|
+
end
|
7
|
+
|
8
|
+
MAX_SIX_LETTERS = ->(move) do
|
9
|
+
move.word.length <= 6 ? move.word.length + move.score[:covered] : 0
|
10
|
+
end
|
11
|
+
|
12
|
+
MAX_FIVE_LETTERS = ->(move) do
|
13
|
+
move.word.length <= 5 ? move.word.length + move.score[:covered] : 0
|
14
|
+
end
|
15
|
+
|
16
|
+
MAX_THREE_LETTERS = ->(move) do
|
17
|
+
move.word.length <= 3 ? move.word.length + move.score[:covered] : 0
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :scoring_algorithm
|
21
|
+
|
22
|
+
def initialize(scoring_algorithm)
|
23
|
+
self.scoring_algorithm = scoring_algorithm
|
24
|
+
end
|
25
|
+
|
26
|
+
def score(move)
|
27
|
+
scoring_algorithm.call move
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/linotype/tile.rb
CHANGED
@@ -19,6 +19,10 @@ module Linotype
|
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|
22
|
+
def board
|
23
|
+
@board
|
24
|
+
end
|
25
|
+
|
22
26
|
def to_a
|
23
27
|
[row, column]
|
24
28
|
end
|
@@ -39,6 +43,14 @@ module Linotype
|
|
39
43
|
@column ||= @board.column(self)
|
40
44
|
end
|
41
45
|
|
46
|
+
def edge?
|
47
|
+
(row == 0 || row == board.row_count - 1) || (column == 0 || column == board.column_count - 1)
|
48
|
+
end
|
49
|
+
|
50
|
+
def corner?
|
51
|
+
(row == 0 || row == board.row_count - 1) && (column == 0 || column == board.column_count - 1)
|
52
|
+
end
|
53
|
+
|
42
54
|
def previous?(coordinate_type)
|
43
55
|
send(coordinate_type) > 0
|
44
56
|
end
|
data/lib/linotype/version.rb
CHANGED
data/lib/linotype.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linotype
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-31 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! ' linotype implements that game mechanic of Letterpress for iOS by
|
15
15
|
atebits software. The program was written to support the automation of letterpress
|
@@ -36,6 +36,8 @@ files:
|
|
36
36
|
- lib/linotype/game.rb
|
37
37
|
- lib/linotype/move.rb
|
38
38
|
- lib/linotype/player.rb
|
39
|
+
- lib/linotype/simulator.rb
|
40
|
+
- lib/linotype/strategy.rb
|
39
41
|
- lib/linotype/tile.rb
|
40
42
|
- lib/linotype/version.rb
|
41
43
|
- linotype.gemspec
|