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 +4 -4
- data/README.md +16 -0
- data/Rakefile +20 -1
- data/bin/2048 +3 -28
- data/features/game_over.feature +10 -0
- data/features/step_definitions/2048_steps.rb +14 -7
- data/features/support/env.rb +3 -0
- data/lib/text2048/app.rb +64 -0
- data/lib/text2048/board.rb +51 -45
- data/lib/text2048/curses_tile.rb +55 -59
- data/lib/text2048/curses_view.rb +82 -49
- data/lib/text2048/monkey_patch/array/tile.rb +1 -0
- data/lib/text2048/text_view.rb +17 -6
- data/lib/text2048/tile.rb +1 -0
- data/lib/text2048/tiles.rb +1 -4
- data/lib/text2048/version.rb +2 -2
- data/lib/text2048.rb +2 -1
- data/spec/spec_helper.rb +3 -0
- data/spec/text2048/board_spec.rb +18 -27
- data/text2048.gemspec +2 -0
- metadata +5 -4
- data/lib/text2048/game.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0e75242c094ba3e75243ce53f9c089c09da2e24
|
4
|
+
data.tar.gz: a0410d80241c4e0067873cc4fe1bfe9fe9acdb09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1e94bc8de48cb973501983cf4c131b091362589c4c2eca09052a689111660e58557b6b344dea329641fb40b03b55477817e8191df01a28a2425bf1e7d396a4e
|
7
|
+
data.tar.gz: 421b6c1f62274af76bfa224c1605c4f31f81bb337586251fd667c43c2ec0a932c0f1fefc28ab6bfa969f7abac98919f43dbcb4fd0506e74488a4e811e9c3a95c
|
data/README.md
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
text2048
|
2
2
|
========
|
3
|
+
[][gem]
|
4
|
+
[][travis]
|
5
|
+
[][codeclimate]
|
6
|
+
[][coveralls]
|
7
|
+
[][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: [:
|
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 '
|
6
|
-
require 'text2048'
|
6
|
+
require 'text2048/app'
|
7
7
|
|
8
|
-
|
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
|
@@ -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
|
-
@
|
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
|
-
@
|
14
|
+
@board = @board.right
|
12
15
|
end
|
13
16
|
|
14
17
|
When(/^I move the board to the left$/) do
|
15
|
-
@
|
18
|
+
@board = @board.left
|
16
19
|
end
|
17
20
|
|
18
21
|
When(/^I move the board up$/) do
|
19
|
-
@
|
22
|
+
@board = @board.up
|
20
23
|
end
|
21
24
|
|
22
25
|
When(/^I move the board down$/) do
|
23
|
-
@
|
26
|
+
@board = @board.down
|
24
27
|
end
|
25
28
|
|
26
29
|
Then(/^the board is:$/) do |string|
|
27
|
-
@
|
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
|
-
@
|
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
|
data/features/support/env.rb
CHANGED
data/lib/text2048/app.rb
ADDED
@@ -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
|
data/lib/text2048/board.rb
CHANGED
@@ -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
|
-
@
|
13
|
+
def initialize(tiles = nil, score = 0)
|
14
|
+
@score = score
|
13
15
|
if tiles
|
14
|
-
|
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
|
31
|
-
|
33
|
+
def win?
|
34
|
+
numbers.select do |each|
|
35
|
+
each.to_i >= 2048
|
36
|
+
end.size > 0
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
|
39
|
+
def lose?
|
40
|
+
right.left.up.down.numbers.size == 4 * 4
|
36
41
|
end
|
37
42
|
|
38
|
-
def
|
39
|
-
|
43
|
+
def left
|
44
|
+
tiles, score = move(:left)
|
45
|
+
self.class.new tiles, @score + score
|
40
46
|
end
|
41
47
|
|
42
|
-
def
|
43
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
75
|
-
|
76
|
-
if @tiles[
|
77
|
-
@tiles[
|
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
|
94
|
+
def move(direction)
|
92
95
|
score = 0
|
93
|
-
@tiles.map
|
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
|
data/lib/text2048/curses_tile.rb
CHANGED
@@ -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,
|
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
|
-
|
29
|
-
|
31
|
+
fill_tile_color
|
32
|
+
draw_number
|
30
33
|
self
|
31
34
|
end
|
32
35
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
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
|
-
|
45
|
-
|
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
|
49
|
-
|
50
|
-
refresh
|
51
|
+
def fill_tile_color
|
52
|
+
attron(color_pair(@color + 100)) { fill }
|
51
53
|
end
|
52
54
|
|
53
|
-
def
|
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
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
68
|
+
private
|
68
69
|
|
69
|
-
|
70
|
+
def box_upper_left
|
71
|
+
[@line - 1, @col - 1]
|
70
72
|
end
|
71
73
|
|
72
|
-
def
|
73
|
-
|
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
|
-
|
78
|
+
def box_lower_left
|
79
|
+
[@line + @height, @col - 1]
|
80
|
+
end
|
79
81
|
|
80
|
-
def
|
81
|
-
|
82
|
+
def box_lower_right
|
83
|
+
[@line + @height, @col + @width]
|
82
84
|
end
|
83
85
|
|
84
|
-
def
|
85
|
-
(
|
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
|
89
|
-
setpos(
|
90
|
-
addstr(
|
93
|
+
def draw_horizonal_line(line, col, length)
|
94
|
+
setpos(line, col)
|
95
|
+
addstr('-' * length)
|
96
|
+
end
|
91
97
|
|
92
|
-
|
93
|
-
|
94
|
-
|
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 |
|
105
|
-
setpos(
|
106
|
-
if @value != 0 &&
|
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
|
data/lib/text2048/curses_view.rb
CHANGED
@@ -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
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
44
|
-
|
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
|
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(
|
89
|
+
update(board)
|
55
90
|
end
|
56
91
|
|
57
|
-
def smaller
|
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(
|
97
|
+
update(board)
|
62
98
|
end
|
63
99
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
116
|
-
tiles.each_with_index
|
117
|
-
|
118
|
-
|
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
|
data/lib/text2048/text_view.rb
CHANGED
@@ -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
|
-
|
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(
|
9
|
-
|
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
data/lib/text2048/tiles.rb
CHANGED
@@ -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
|
data/lib/text2048/version.rb
CHANGED
data/lib/text2048.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/text2048/board_spec.rb
CHANGED
@@ -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
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
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.
|
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-
|
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
|