text2048 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: e85082a7a9811119fd638e5dc7e163c670ea45fb
4
- data.tar.gz: 6072216598424d89bc4ac20b4c8c62b507b87a8b
3
+ metadata.gz: b0e75242c094ba3e75243ce53f9c089c09da2e24
4
+ data.tar.gz: a0410d80241c4e0067873cc4fe1bfe9fe9acdb09
5
5
  SHA512:
6
- metadata.gz: dbf18ee75d43a5c61dbe9c866f0fdcef642b7321db7fe7d1e4d441acab10f517d1bce4a0c8aa5b6cee8e82510bd95a5e2cff3de3f0db4ecdc0eee737cd892ae2
7
- data.tar.gz: 30a1bca88794b26870d799cc037339dbf6214672a54a65da00221a69871125982628dca00f974c52ef4ce409f207b2dc9ece7b6636171769a51d64e17399d7af
6
+ metadata.gz: a1e94bc8de48cb973501983cf4c131b091362589c4c2eca09052a689111660e58557b6b344dea329641fb40b03b55477817e8191df01a28a2425bf1e7d396a4e
7
+ data.tar.gz: 421b6c1f62274af76bfa224c1605c4f31f81bb337586251fd667c43c2ec0a932c0f1fefc28ab6bfa969f7abac98919f43dbcb4fd0506e74488a4e811e9c3a95c
data/README.md CHANGED
@@ -1,8 +1,19 @@
1
1
  text2048
2
2
  ========
3
+ [![Gem Version](http://img.shields.io/gem/v/text2048.svg)][gem]
4
+ [![Build Status](http://img.shields.io/travis/yasuhito/text2048/develop.svg)][travis]
5
+ [![Code Climate](http://img.shields.io/codeclimate/github/yasuhito/text2048.svg)][codeclimate]
6
+ [![Coverage Status](http://img.shields.io/coveralls/yasuhito/text2048/develop.svg)][coveralls]
7
+ [![Dependency Status](http://img.shields.io/gemnasium/yasuhito/text2048.svg)][gemnasium]
3
8
 
4
9
  Text mode 2048 game.
5
10
 
11
+ [gem]: https://rubygems.org/gems/text2048
12
+ [travis]: http://travis-ci.org/yasuhito/text2048
13
+ [codeclimate]: https://codeclimate.com/github/yasuhito/text2048
14
+ [coveralls]: https://coveralls.io/r/yasuhito/text2048?branch=develop
15
+ [gemnasium]: https://gemnasium.com/yasuhito/text2048
16
+
6
17
  How to Play
7
18
  ===========
8
19
 
@@ -22,3 +33,8 @@ Installation
22
33
  ```
23
34
  $ gem install text2048
24
35
  ```
36
+
37
+ Link
38
+ ====
39
+
40
+ * [The official version of 2048](http://gabrielecirulli.github.io/2048/) by Gabriele Cirulli
data/Rakefile CHANGED
@@ -1,8 +1,13 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'bundler/gem_tasks'
4
+ require 'coveralls/rake/task'
4
5
 
5
- task default: [:spec, :cucumber, :rubocop]
6
+ task default: [:test, :reek, :rubocop]
7
+ task test: [:spec, :cucumber, 'coveralls:push']
8
+ task travis: :default
9
+
10
+ Coveralls::RakeTask.new
6
11
 
7
12
  begin
8
13
  require 'rspec/core/rake_task'
@@ -30,3 +35,17 @@ rescue LoadError
30
35
  $stderr.puts 'Rubocop is disabled'
31
36
  end
32
37
  end
38
+
39
+ begin
40
+ require 'reek/rake/task'
41
+ Reek::Rake::Task.new do |t|
42
+ t.fail_on_error = false
43
+ t.verbose = false
44
+ t.reek_opts = '--quiet'
45
+ t.source_files = FileList['lib/**/*.rb']
46
+ end
47
+ rescue LoadError
48
+ task :reek do
49
+ $stderr.puts 'Reek is disabled'
50
+ end
51
+ end
data/bin/2048 CHANGED
@@ -1,33 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # encoding: utf-8
2
3
 
3
4
  $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
4
5
 
5
- require 'curses'
6
- require 'text2048'
6
+ require 'text2048/app'
7
7
 
8
- include Curses
9
-
10
- KEYS = {
11
- 'h' => :left!, 'l' => :right!, 'k' => :up!, 'j' => :down!,
12
- Key::LEFT => :left!, Key::RIGHT => :right!,
13
- Key::UP => :up!, Key::DOWN => :down!,
14
- '+' => :larger!, '-' => :smaller!,
15
- 'q' => :quit
16
- }
17
-
18
- game = Text2048::Game.new(Text2048::CursesView.new)
19
- game.start
20
- game.draw
21
-
22
- loop do
23
- game.game_over if game.lose?
24
- command = KEYS[Curses.getch]
25
- game.input(command) if command
26
- case command
27
- when :left!, :right!, :up!, :down!
28
- game.generate
29
- game.draw
30
- else
31
- # noop
32
- end
33
- end
8
+ Text2048::App.new.start
@@ -0,0 +1,10 @@
1
+ Feature: Game Over
2
+ Scenario: Game Over
3
+ When a board:
4
+ """
5
+ 2 4 8 16
6
+ 4 8 16 32
7
+ 8 16 32 64
8
+ 16 32 64 128
9
+ """
10
+ Then it is game over
@@ -1,33 +1,40 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'text2048'
2
4
 
3
5
  Given(/^a board:$/) do |string|
4
6
  layout = string.split("\n").reduce([]) do |result, row|
5
7
  result << row.split(' ').map(&:to_i)
6
8
  end
7
- @game = Text2048::Game.new(Text2048::TextView.new(output), layout)
9
+ @view = Text2048::TextView.new(output)
10
+ @board = Text2048::Board.new(layout)
8
11
  end
9
12
 
10
13
  When(/^I move the board to the right$/) do
11
- @game.right!
14
+ @board = @board.right
12
15
  end
13
16
 
14
17
  When(/^I move the board to the left$/) do
15
- @game.left!
18
+ @board = @board.left
16
19
  end
17
20
 
18
21
  When(/^I move the board up$/) do
19
- @game.up!
22
+ @board = @board.up
20
23
  end
21
24
 
22
25
  When(/^I move the board down$/) do
23
- @game.down!
26
+ @board = @board.down
24
27
  end
25
28
 
26
29
  Then(/^the board is:$/) do |string|
27
- @game.draw
30
+ @view.update(@board)
28
31
  output.messages.should eq(string)
29
32
  end
30
33
 
31
34
  Then(/^the score is (\d+)$/) do |score|
32
- @game.score.should eq(score.to_i)
35
+ @board.score.should eq(score.to_i)
36
+ end
37
+
38
+ Then(/^it is game over$/) do
39
+ @board.lose?.should be_true
33
40
  end
@@ -1,5 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'coveralls'
4
+ Coveralls.wear_merged!
5
+
3
6
  # Dummy output
4
7
  class Output
5
8
  def messages
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+
3
+ require 'curses'
4
+ require 'text2048'
5
+
6
+ # This module smells of :reek:UncommunicativeModuleName
7
+ module Text2048
8
+ # Controller class.
9
+ class App
10
+ include Curses
11
+
12
+ KEYS = {
13
+ 'h' => :left, 'l' => :right, 'k' => :up, 'j' => :down,
14
+ Key::LEFT => :left, Key::RIGHT => :right,
15
+ Key::UP => :up, Key::DOWN => :down,
16
+ '+' => :larger, '-' => :smaller,
17
+ 'q' => :quit
18
+ }
19
+
20
+ def initialize
21
+ @view = CursesView.new
22
+ @board = Board.new
23
+ @view.update(@board)
24
+ end
25
+
26
+ def start
27
+ loop do
28
+ @view.win if @board.win?
29
+ @view.game_over if @board.lose?
30
+ input KEYS[Curses.getch]
31
+ end
32
+ sleep
33
+ end
34
+
35
+ private
36
+
37
+ def input(command)
38
+ case command
39
+ when :left, :right, :up, :down
40
+ move_and_generate(command)
41
+ when :larger, :smaller
42
+ @view.__send__ command, @board
43
+ when :quit
44
+ exit 0
45
+ end
46
+ end
47
+
48
+ def move_and_generate(command)
49
+ last = move(command)
50
+ generate if @board != last
51
+ end
52
+
53
+ def move(command)
54
+ last = @board.dup
55
+ @board = @board.__send__(command)
56
+ last
57
+ end
58
+
59
+ def generate
60
+ @board.generate
61
+ @view.update(@board)
62
+ end
63
+ end
64
+ end
@@ -3,16 +3,19 @@
3
3
  require 'text2048/tile'
4
4
  require 'text2048/tiles'
5
5
 
6
+ # This module smells of :reek:UncommunicativeModuleName
6
7
  module Text2048
7
8
  # Game board
8
9
  class Board
10
+ attr_reader :score
9
11
  attr_reader :tiles
10
12
 
11
- def initialize(tiles = nil)
12
- @tiles = Array.new(4) { Array.new(4) { Tile.new(0) } }
13
+ def initialize(tiles = nil, score = 0)
14
+ @score = score
13
15
  if tiles
14
- load_tiles(tiles)
16
+ @tiles = tiles.dup
15
17
  else
18
+ @tiles = Array.new(4) { Array.new(4) { Tile.new(0) } }
16
19
  2.times { generate }
17
20
  end
18
21
  end
@@ -27,54 +30,54 @@ module Text2048
27
30
  end
28
31
  end
29
32
 
30
- def right!
31
- move! :right
33
+ def win?
34
+ numbers.select do |each|
35
+ each.to_i >= 2048
36
+ end.size > 0
32
37
  end
33
38
 
34
- def left!
35
- move! :left
39
+ def lose?
40
+ right.left.up.down.numbers.size == 4 * 4
36
41
  end
37
42
 
38
- def up!
39
- transpose { move! :left }
43
+ def left
44
+ tiles, score = move(:left)
45
+ self.class.new tiles, @score + score
40
46
  end
41
47
 
42
- def down!
43
- transpose { move! :right }
48
+ def right
49
+ tiles, score = move(:right)
50
+ self.class.new tiles, @score + score
51
+ end
52
+
53
+ def up
54
+ tiles, score = transpose { move(:left) }
55
+ self.class.new tiles, @score + score
56
+ end
57
+
58
+ def down
59
+ tiles, score = transpose { move(:right) }
60
+ self.class.new tiles, @score + score
44
61
  end
45
62
 
46
63
  def ==(other)
47
- @tiles.zip(other.tiles).reduce(true) do |result, each|
48
- result && Tiles.new(each[0]) == Tiles.new(each[1])
49
- end
64
+ layout == other.layout
50
65
  end
51
66
 
52
67
  def merged_tiles
53
- result = []
54
- @tiles.each_with_index do |row, y|
55
- row.each_with_index do |each, x|
56
- result << [y, x] if each.status == :merged
57
- end
58
- end
59
- result
68
+ find_tiles :merged
60
69
  end
61
70
 
62
71
  def generated_tiles
63
- result = []
64
- @tiles.each_with_index do |row, y|
65
- row.each_with_index do |each, x|
66
- result << [y, x] if each.status == :generated
67
- end
68
- end
69
- result
72
+ find_tiles :generated
70
73
  end
71
74
 
72
75
  def generate
73
76
  loop do
74
- x = rand(4)
75
- y = rand(4)
76
- if @tiles[y][x] == 0
77
- @tiles[y][x] = Tile.new(rand < 0.8 ? 2 : 4, :generated)
77
+ line = rand(4)
78
+ col = rand(4)
79
+ if @tiles[line][col] == 0
80
+ @tiles[line][col] = Tile.new(rand < 0.8 ? 2 : 4, :generated)
78
81
  return
79
82
  end
80
83
  end
@@ -88,29 +91,32 @@ module Text2048
88
91
 
89
92
  private
90
93
 
91
- def move!(direction)
94
+ def move(direction)
92
95
  score = 0
93
- @tiles.map! do |each|
96
+ tiles = @tiles.map do |each|
94
97
  row, sc = Tiles.new(each).__send__ direction
95
98
  score += sc
96
99
  row
97
100
  end
98
- score
101
+ [tiles, score]
99
102
  end
100
103
 
104
+ def find_tiles(status)
105
+ list = []
106
+ @tiles.each_with_index do |row, line|
107
+ row.each_with_index do |each, col|
108
+ list << [line, col] if each.status == status
109
+ end
110
+ end
111
+ list
112
+ end
113
+
114
+ # FIXME: this method is destructive.
101
115
  def transpose
102
116
  @tiles = @tiles.transpose
103
- score = yield
117
+ @tiles, score = yield
104
118
  @tiles = @tiles.transpose
105
- score
106
- end
107
-
108
- def load_tiles(tiles)
109
- tiles.each_with_index do |row, y|
110
- row.each_with_index do |number, x|
111
- @tiles[y][x] = Tile.new(number)
112
- end
113
- end
119
+ [@tiles, score]
114
120
  end
115
121
  end
116
122
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'curses'
4
4
 
5
+ # This module smells of :reek:UncommunicativeModuleName
5
6
  module Text2048
6
7
  # Shows tiles in curses.
7
8
  class CursesTile
@@ -14,107 +15,102 @@ module Text2048
14
15
  DEFAULT_HEIGHT = 3
15
16
  DEFAULT_WIDTH = 5
16
17
 
17
- def initialize(value, y, x, color, scale = 1)
18
+ def initialize(value, line, col, color, scale = 1)
18
19
  @value = value.to_i
19
- @y = y
20
- @x = x
21
- @color = color
22
20
  @height = (DEFAULT_HEIGHT * scale).to_i
21
+ @box_height = @height + 2
23
22
  @width = (DEFAULT_WIDTH * scale).to_i
23
+ @box_width = @width + 2
24
+ @line = (@height + 1) * line + 2
25
+ @col = (@width + 1) * col + 1
26
+ @color = color
24
27
  end
25
28
 
26
29
  def show
27
30
  draw_box
28
- attron(color_pair(@color + 100)) { fill }
29
- attron(color_pair(@color)) { draw_number }
31
+ fill_tile_color
32
+ draw_number
30
33
  self
31
34
  end
32
35
 
33
- def pop1
34
- setpos(yc - 1, xc - 1)
35
- addstr('.' * (@width + 2))
36
-
37
- (0..(@height - 1)).each do |dy|
38
- setpos(yc + dy, xc - 1)
39
- addstr('.')
40
- setpos(yc + dy, xc + @width)
41
- addstr('.')
36
+ def pop
37
+ attron(color_pair(@color + 100)) do
38
+ draw_box
42
39
  end
40
+ end
43
41
 
44
- setpos(yc + @height, xc - 1)
45
- addstr('.' * (@width + 2))
42
+ def draw_box
43
+ draw_square
44
+ [box_upper_left, box_upper_right,
45
+ box_lower_left, box_lower_right].each do |line, col|
46
+ setpos(line, col)
47
+ addstr('+')
48
+ end
46
49
  end
47
50
 
48
- def pop2
49
- draw_box
50
- refresh
51
+ def fill_tile_color
52
+ attron(color_pair(@color + 100)) { fill }
51
53
  end
52
54
 
53
- def zoom1
55
+ def fill_black
54
56
  attron(color_pair(COLOR_BLACK + 100)) { fill }
55
- attron(color_pair(@color)) { draw_number }
56
57
  refresh
57
58
  end
58
59
 
59
- def zoom2
60
- setpos(yc + @height / 2 - 1, xc + @width / 2 - 1)
61
- attron(color_pair(@color + 100)) { addstr('...') }
62
- setpos(yc + @height / 2, xc + @width / 2 - 1)
63
- attron(color_pair(@color + 100)) { addstr('...') }
64
- setpos(yc + @height / 2 + 1, xc + @width / 2 - 1)
65
- attron(color_pair(@color + 100)) { addstr('...') }
60
+ def draw_number
61
+ return if @value == 0
62
+ setpos(@line + @height / 2, @col)
63
+ attron(color_pair(@color)) do
64
+ addstr @value.to_s.center(@width)
65
+ end
66
+ end
66
67
 
67
- attron(color_pair(@color)) { draw_number }
68
+ private
68
69
 
69
- refresh
70
+ def box_upper_left
71
+ [@line - 1, @col - 1]
70
72
  end
71
73
 
72
- def zoom3
73
- attron(color_pair(@color + 100)) { fill }
74
- attron(color_pair(@color)) { draw_number }
75
- refresh
74
+ def box_upper_right
75
+ [@line - 1, @col + @width]
76
76
  end
77
77
 
78
- private
78
+ def box_lower_left
79
+ [@line + @height, @col - 1]
80
+ end
79
81
 
80
- def yc
81
- (@height + 1) * @y + 2
82
+ def box_lower_right
83
+ [@line + @height, @col + @width]
82
84
  end
83
85
 
84
- def xc
85
- (@width + 1) * @x + 1
86
+ def draw_square
87
+ draw_horizonal_line(*box_upper_left, @box_width)
88
+ draw_vertical_line(*box_upper_left, @box_height)
89
+ draw_vertical_line(*box_upper_right, @box_height)
90
+ draw_horizonal_line(*box_lower_left, @box_width)
86
91
  end
87
92
 
88
- def draw_box
89
- setpos(yc - 1, xc - 1)
90
- addstr("+#{'-' * @width}+")
93
+ def draw_horizonal_line(line, col, length)
94
+ setpos(line, col)
95
+ addstr('-' * length)
96
+ end
91
97
 
92
- (0..(@height - 1)).each do |dy|
93
- setpos(yc + dy, xc - 1)
94
- addstr('|')
95
- setpos(yc + dy, xc + @width)
98
+ def draw_vertical_line(line, col, length)
99
+ (0..(length - 1)).each do |each|
100
+ setpos(line + each, col)
96
101
  addstr('|')
97
102
  end
98
-
99
- setpos(yc + @height, xc - 1)
100
- addstr("+#{'-' * @width}+")
101
103
  end
102
104
 
103
105
  def fill
104
- (0..(@height - 1)).each do |dy|
105
- setpos(yc + dy, xc)
106
- if @value != 0 && dy == @height / 2
106
+ (0..(@height - 1)).each do |each|
107
+ setpos(@line + each, @col)
108
+ if @value != 0 && each == @height / 2
107
109
  addstr @value.to_s.center(@width)
108
110
  else
109
111
  addstr('.' * @width)
110
112
  end
111
113
  end
112
114
  end
113
-
114
- def draw_number
115
- return if @value == 0
116
- setpos(yc + @height / 2, xc)
117
- addstr @value.to_s.center(@width)
118
- end
119
115
  end
120
116
  end
@@ -3,10 +3,41 @@
3
3
  require 'curses'
4
4
  require 'text2048/curses_tile'
5
5
 
6
+ # This module smells of :reek:UncommunicativeModuleName
6
7
  module Text2048
7
8
  # Curses UI
8
9
  class CursesView
10
+ # Curses tile effects
11
+ module TileEffects
12
+ def pop_tiles(list)
13
+ pop(list)
14
+ refresh
15
+ sleep 0.1
16
+ draw_box(list)
17
+ refresh
18
+ end
19
+
20
+ def zoom_tiles(list)
21
+ [:fill_black, :draw_number, :show].each do |each|
22
+ list.each { |line, col| @tiles[line][col].__send__ each }
23
+ refresh
24
+ sleep 0.05
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def pop(list)
31
+ list.each { |line, col| @tiles[line][col].pop }
32
+ end
33
+
34
+ def draw_box(list)
35
+ list.each { |line, col| @tiles[line][col].draw_box }
36
+ end
37
+ end
38
+
9
39
  include Curses
40
+ include TileEffects
10
41
 
11
42
  COLORS = {
12
43
  0 => COLOR_BLACK,
@@ -23,6 +54,9 @@ module Text2048
23
54
  2048 => COLOR_RED
24
55
  }
25
56
 
57
+ DEFAULT_WIDTH = (CursesTile::DEFAULT_WIDTH + 1) * 4 + 1
58
+ DEFAULT_HEIGHT = (CursesTile::DEFAULT_HEIGHT + 1) * 4 + 2
59
+
26
60
  def initialize
27
61
  @tiles = Array.new(4) { Array.new(4) }
28
62
  @scale = 2
@@ -30,70 +64,66 @@ module Text2048
30
64
  @scale_step = 0.5
31
65
  end
32
66
 
33
- def start
34
- init_screen
35
- curs_set(0)
36
- start_color
37
- stdscr.keypad(true)
38
- noecho
39
- init_color_pairs
40
- at_exit { close_screen }
67
+ def update(board)
68
+ maybe_init_curses
69
+ draw_score(board.score)
70
+ draw_tiles(board.tiles)
71
+ refresh
72
+ pop_tiles(board.merged_tiles)
73
+ zoom_tiles(board.generated_tiles)
41
74
  end
42
75
 
43
- def update(tiles, score)
44
- draw_score(score)
45
- tiles.each_with_index do |row, y|
46
- draw_row(row, y)
47
- end
76
+ def height
77
+ (@tiles[0][0].height + 1) * 4 + 2
48
78
  end
49
79
 
50
- def larger!(tiles, score)
80
+ def width
81
+ (@tiles[0][0].width + 1) * 4 + 1
82
+ end
83
+
84
+ def larger(board)
51
85
  return if @scale > scale_max
86
+ maybe_init_curses
52
87
  @scale += @scale_step
53
88
  clear
54
- update(tiles, score)
89
+ update(board)
55
90
  end
56
91
 
57
- def smaller!(tiles, score)
92
+ def smaller(board)
58
93
  return if @scale <= @scale_min
94
+ maybe_init_curses
59
95
  @scale -= @scale_step
60
96
  clear
61
- update(tiles, score)
97
+ update(board)
62
98
  end
63
99
 
64
- def pop_tiles(list)
65
- list.each do |y, x|
66
- attron(color_pair(@tiles[y][x].color + 100)) do
67
- @tiles[y][x].pop1
68
- end
69
- end
70
- refresh
71
- sleep 0.1
72
-
73
- list.each do |y, x|
74
- @tiles[y][x].pop2
75
- end
76
- end
77
-
78
- def zoom_tiles(list)
79
- [:zoom1, :zoom2, :zoom3].each do |each|
80
- list.each do |y, x|
81
- @tiles[y][x].__send__ each
82
- end
83
- sleep 0.05
84
- end
100
+ def win
101
+ setpos(height / 2, width / 2 - 1)
102
+ attron(color_pair(COLOR_RED)) { addstr('WIN!') }
85
103
  end
86
104
 
87
105
  def game_over
88
- width = (@tiles[0][0].width + 1) * 4 + 1
89
- height = (@tiles[0][0].height + 1) * 4 + 2
90
-
91
106
  setpos(height / 2, width / 2 - 4)
92
107
  attron(color_pair(COLOR_RED)) { addstr('GAME OVER') }
93
108
  end
94
109
 
95
110
  private
96
111
 
112
+ def maybe_init_curses
113
+ @curses_initialized || init_curses
114
+ @curses_initialized = true
115
+ end
116
+
117
+ def init_curses
118
+ init_screen
119
+ curs_set(0)
120
+ start_color
121
+ stdscr.keypad(true)
122
+ noecho
123
+ init_color_pairs
124
+ at_exit { close_screen }
125
+ end
126
+
97
127
  def init_color_pairs
98
128
  COLORS.each_pair do |_key, value|
99
129
  init_pair value, COLOR_BLACK, value
@@ -102,9 +132,9 @@ module Text2048
102
132
  end
103
133
 
104
134
  def scale_max
105
- w = (CursesTile::DEFAULT_WIDTH + 1) * 4 + 1
106
- h = (CursesTile::DEFAULT_HEIGHT + 1) * 4 + 2
107
- (cols - 1) / w < lines / h ? (cols - 1) / w : lines / h
135
+ ratio_width = (cols - 1) / DEFAULT_WIDTH
136
+ ratio_height = lines / DEFAULT_HEIGHT
137
+ ratio_width < ratio_height ? ratio_width : ratio_height
108
138
  end
109
139
 
110
140
  def draw_score(score)
@@ -112,12 +142,15 @@ module Text2048
112
142
  addstr("Score: #{score}")
113
143
  end
114
144
 
115
- def draw_row(tiles, y)
116
- tiles.each_with_index do |each, x|
117
- @tiles[y][x] =
118
- CursesTile.new(each, y, x, COLORS[each.to_i], @scale).show
145
+ def draw_tiles(tiles)
146
+ tiles.each_with_index { |row, line| draw_row(row, line) }
147
+ end
148
+
149
+ def draw_row(tiles, line)
150
+ tiles.each_with_index do |each, col|
151
+ @tiles[line][col] =
152
+ CursesTile.new(each, line, col, COLORS[each.to_i], @scale).show
119
153
  end
120
- refresh
121
154
  end
122
155
  end
123
156
  end
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # This module smells of :reek:UncommunicativeModuleName
3
4
  module Text2048
4
5
  module MonkeyPatch
5
6
  module Array
@@ -1,15 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ # This module smells of :reek:UncommunicativeModuleName
1
4
  module Text2048
2
5
  # Simple text view.
3
6
  class TextView
4
- def initialize(output)
7
+ # Board row in text.
8
+ class Row
9
+ def initialize(row)
10
+ @row = row
11
+ end
12
+
13
+ def to_s
14
+ @row.map { |each| each != 0 ? each : '_' }.join(' ')
15
+ end
16
+ end
17
+
18
+ def initialize(output = STDOUT)
5
19
  @output = output
6
20
  end
7
21
 
8
- def update(layout, _score)
9
- string = layout.map do |row|
10
- row.map { |num| num != 0 ? num : '_' }.join(' ')
11
- end.join("\n")
12
- @output.puts string
22
+ def update(game)
23
+ @output.puts game.tiles.map { |row| Row.new(row).to_s }.join("\n")
13
24
  end
14
25
 
15
26
  def pop_tiles(_list)
data/lib/text2048/tile.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'forwardable'
4
4
 
5
+ # This module smells of :reek:UncommunicativeModuleName
5
6
  module Text2048
6
7
  # 2048 tile
7
8
  class Tile
@@ -4,6 +4,7 @@ require 'forwardable'
4
4
  require 'text2048/monkey_patch/array'
5
5
  require 'text2048/tile'
6
6
 
7
+ # This module smells of :reek:UncommunicativeModuleName
7
8
  module Text2048
8
9
  # Each row or column of a game board.
9
10
  class Tiles
@@ -23,10 +24,6 @@ module Text2048
23
24
  [list.rshrink.reverse, score]
24
25
  end
25
26
 
26
- def ==(other)
27
- rshrink == other.rshrink
28
- end
29
-
30
27
  def_delegators :@list, :map, :rshrink
31
28
  end
32
29
  end
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- # Base module.
3
+ # This module smells of :reek:UncommunicativeModuleName
4
4
  module Text2048
5
5
  # gem version.
6
- VERSION = '0.1.0'.freeze
6
+ VERSION = '0.2.0'.freeze
7
7
  end
data/lib/text2048.rb CHANGED
@@ -1,4 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'text2048/board'
2
4
  require 'text2048/curses_view'
3
- require 'text2048/game'
4
5
  require 'text2048/text_view'
data/spec/spec_helper.rb CHANGED
@@ -2,5 +2,8 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
4
 
5
+ require 'coveralls'
6
+ Coveralls.wear_merged!
7
+
5
8
  require 'rspec'
6
9
  require 'rspec/given'
@@ -1,7 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'text2048'
2
4
 
3
5
  describe Text2048::Board, '.new' do
4
- context 'with no numbers' do
6
+ context 'with all zeroes' do
5
7
  Given(:board) do
6
8
  Text2048::Board.new([[0, 0, 0, 0],
7
9
  [0, 0, 0, 0],
@@ -10,13 +12,13 @@ describe Text2048::Board, '.new' do
10
12
  end
11
13
 
12
14
  describe '#right' do
13
- When { board.right! }
15
+ When(:result) { board.right }
14
16
 
15
17
  Then do
16
- board.layout == [[0, 0, 0, 0],
17
- [0, 0, 0, 0],
18
- [0, 0, 0, 0],
19
- [0, 0, 0, 0]]
18
+ result.layout == [[0, 0, 0, 0],
19
+ [0, 0, 0, 0],
20
+ [0, 0, 0, 0],
21
+ [0, 0, 0, 0]]
20
22
  end
21
23
  end
22
24
  end
@@ -30,26 +32,15 @@ describe Text2048::Board, '.new' do
30
32
  end
31
33
 
32
34
  describe '#right' do
33
- When { board.right! }
35
+ When(:result) { board.right }
34
36
 
35
37
  Then do
36
- board.layout == [[0, 0, 0, 2],
37
- [0, 0, 0, 2],
38
- [0, 0, 0, 2],
39
- [0, 0, 0, 2]]
38
+ result.layout == [[0, 0, 0, 2],
39
+ [0, 0, 0, 2],
40
+ [0, 0, 0, 2],
41
+ [0, 0, 0, 2]]
40
42
  end
41
43
  end
42
-
43
- describe '#==' do
44
- When(:result) do
45
- board == Text2048::Board.new([[0, 0, 0, 2],
46
- [0, 0, 2, 0],
47
- [0, 2, 0, 0],
48
- [2, 0, 0, 0]])
49
- end
50
-
51
- Then { result == true }
52
- end
53
44
  end
54
45
 
55
46
  context 'with six 2s that can be merged' do
@@ -61,13 +52,13 @@ describe Text2048::Board, '.new' do
61
52
  end
62
53
 
63
54
  describe '#right' do
64
- When { board.right! }
55
+ When(:result) { board.right }
65
56
 
66
57
  Then do
67
- board.layout == [[0, 0, 0, 4],
68
- [0, 0, 0, 2],
69
- [0, 0, 0, 4],
70
- [0, 0, 0, 2]]
58
+ result.layout == [[0, 0, 0, 4],
59
+ [0, 0, 0, 2],
60
+ [0, 0, 0, 4],
61
+ [0, 0, 0, 2]]
71
62
  end
72
63
  end
73
64
  end
data/text2048.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  lib = File.expand_path('../lib', __FILE__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'text2048/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: text2048
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
  - Yasuhito Takamiya
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-02 00:00:00.000000000 Z
11
+ date: 2014-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -40,6 +40,7 @@ files:
40
40
  - README.md
41
41
  - Rakefile
42
42
  - bin/2048
43
+ - features/game_over.feature
43
44
  - features/move_down.feature
44
45
  - features/move_left.feature
45
46
  - features/move_right.feature
@@ -47,10 +48,10 @@ files:
47
48
  - features/step_definitions/2048_steps.rb
48
49
  - features/support/env.rb
49
50
  - lib/text2048.rb
51
+ - lib/text2048/app.rb
50
52
  - lib/text2048/board.rb
51
53
  - lib/text2048/curses_tile.rb
52
54
  - lib/text2048/curses_view.rb
53
- - lib/text2048/game.rb
54
55
  - lib/text2048/monkey_patch/array.rb
55
56
  - lib/text2048/monkey_patch/array/tile.rb
56
57
  - lib/text2048/text_view.rb
@@ -89,10 +90,10 @@ test_files:
89
90
  - spec/spec_helper.rb
90
91
  - spec/text2048/board_spec.rb
91
92
  - spec/text2048/tiles_spec.rb
93
+ - features/game_over.feature
92
94
  - features/move_down.feature
93
95
  - features/move_left.feature
94
96
  - features/move_right.feature
95
97
  - features/move_up.feature
96
98
  - features/step_definitions/2048_steps.rb
97
99
  - features/support/env.rb
98
- has_rdoc:
data/lib/text2048/game.rb DELETED
@@ -1,81 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'forwardable'
4
- require 'text2048/board'
5
-
6
- module Text2048
7
- # A class responsible of handling all the command line interface
8
- # logic.
9
- class Game
10
- attr_reader :score
11
-
12
- extend Forwardable
13
-
14
- def initialize(view, tiles = nil)
15
- @score = 0
16
- @board = Board.new(tiles)
17
- @view = view
18
- end
19
-
20
- def start
21
- @view.start
22
- end
23
-
24
- def draw
25
- @view.update(@board.tiles, @score)
26
- @view.pop_tiles(@board.merged_tiles)
27
- @view.zoom_tiles(@board.generated_tiles)
28
- end
29
-
30
- def lose?
31
- b = @board.dup
32
- b.right!
33
- b.left!
34
- b.up!
35
- b.down!
36
- b.numbers.size == 4 * 4
37
- end
38
-
39
- def input(command)
40
- @last = @board.tiles.dup
41
- __send__ command
42
- end
43
-
44
- def left!
45
- @score += @board.left!
46
- end
47
-
48
- def right!
49
- @score += @board.right!
50
- end
51
-
52
- def up!
53
- @score += @board.up!
54
- end
55
-
56
- def down!
57
- @score += @board.down!
58
- end
59
-
60
- def generate
61
- return if @last == @board.tiles
62
- @board.generate
63
- end
64
-
65
- def larger!
66
- @view.larger!(@board.tiles, @score)
67
- end
68
-
69
- def smaller!
70
- @view.smaller!(@board.tiles, @score)
71
- end
72
-
73
- def game_over
74
- @view.game_over
75
- end
76
-
77
- def quit
78
- exit 0
79
- end
80
- end
81
- end