linotype 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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 = [Player.new, Player.new]
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
- undo_last_move!
68
+ move.undo!
68
69
  end
69
70
  end
70
- potential_moves.collect(&:to_hash)
71
+ potential_moves
71
72
  end
72
73
 
73
74
  def valid_potential_plays
74
- test_potential_plays.select { |potential_play| potential_play[:valid] }
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 { |a, b| b[:score][:defended] <=> a[:score][:defended] }.first
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
- covered_tiles(nil)
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, :player, :invalid_reason, :score
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
- @player = player
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?
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Linotype
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/linotype.rb CHANGED
@@ -4,8 +4,7 @@ require "linotype/board"
4
4
  require "linotype/move"
5
5
  require "linotype/player"
6
6
  require "linotype/tile"
7
+ require "linotype/strategy"
8
+ require "linotype/simulator"
7
9
  require "linotype/dictionary/dictionary"
8
10
 
9
- module Linotype
10
-
11
- end
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.3
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-30 00:00:00.000000000 Z
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