text2048 0.8.0 → 0.9.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 +12 -10
- data/bin/2048 +11 -0
- data/lib/text2048.rb +1 -0
- data/lib/text2048/app.rb +31 -1
- data/lib/text2048/curses_view.rb +22 -5
- data/lib/text2048/curses_view/keyboard.rb +4 -0
- data/lib/text2048/curses_view/lcd.rb +26 -30
- data/lib/text2048/curses_view/tile_effects.rb +2 -0
- data/lib/text2048/high_score.rb +35 -0
- data/lib/text2048/version.rb +1 -1
- data/spec/text2048/app_spec.rb +26 -12
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9db309419e40329c97f3490cacf5b307023929e5
|
4
|
+
data.tar.gz: 24055097be1b6246422694cb75b2b72685d37253
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8d9031ff6562432a491aab20592ad4f81148522417e1f5ef08a13686bcf13e7ba5301da9d403ad302601ca43cc874532dae8958f43a56658b3fa4fbc1587cf6
|
7
|
+
data.tar.gz: 16d316b287ac2bd945e2e428026ad40990fac2d06fadfd9c742ef5cc7088d1ee9752fe9c23e713dc3337176d3e967551d65d91996cbfde0a6d440372fc2739f1
|
data/README.md
CHANGED
@@ -1,24 +1,25 @@
|
|
1
1
|
text2048
|
2
2
|
========
|
3
|
-
[][gem]
|
4
|
-
[][travis]
|
5
|
-
[][codeclimate]
|
6
|
-
[][gemnasium]
|
8
|
-
[][gem]
|
4
|
+
[][travis]
|
5
|
+
[][codeclimate]
|
6
|
+
[][codeclimate]
|
7
|
+
[][gemnasium]
|
8
|
+
[][gitter]
|
9
|
+
[][gittip]
|
9
10
|
|
10
11
|
Text mode 2048 game.
|
11
12
|
|
12
13
|
[gem]: https://rubygems.org/gems/text2048
|
13
14
|
[travis]: http://travis-ci.org/yasuhito/text2048
|
14
15
|
[codeclimate]: https://codeclimate.com/github/yasuhito/text2048
|
15
|
-
[coveralls]: https://coveralls.io/r/yasuhito/text2048?branch=develop
|
16
16
|
[gemnasium]: https://gemnasium.com/yasuhito/text2048
|
17
|
+
[gitter]: https://gitter.im/yasuhito/text2048
|
17
18
|
[gittip]: https://www.gittip.com/yasuhito/
|
18
19
|
|
19
20
|
[][screenshot]
|
20
21
|
|
21
|
-
[screenshot]: https://
|
22
|
+
[screenshot]: https://asciinema.org/a/9577
|
22
23
|
|
23
24
|
Installation
|
24
25
|
------------
|
@@ -34,8 +35,9 @@ How to Play
|
|
34
35
|
$ 2048
|
35
36
|
```
|
36
37
|
|
37
|
-
- Use your arrow keys to move the tiles.
|
38
|
-
-
|
38
|
+
- Use your arrow keys or vi keys ('h'/'j'/'k'/'l') to move the tiles.
|
39
|
+
- '+'/'-' to increase or decrease the size of the tiles displayed.
|
40
|
+
- 'q' to quit.
|
39
41
|
|
40
42
|
Links
|
41
43
|
-----
|
data/bin/2048
CHANGED
@@ -4,6 +4,17 @@
|
|
4
4
|
$LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
5
5
|
|
6
6
|
require 'text2048/app'
|
7
|
+
require 'timeout'
|
8
|
+
|
9
|
+
app = Text2048::App.new
|
10
|
+
begin
|
11
|
+
timeout(60) { app.show_title }
|
12
|
+
rescue Timeout::Error
|
13
|
+
loop do
|
14
|
+
app.demo
|
15
|
+
break if app.wait_any_key(0.5)
|
16
|
+
end
|
17
|
+
end
|
7
18
|
|
8
19
|
app = Text2048::App.new
|
9
20
|
app.generate(2)
|
data/lib/text2048.rb
CHANGED
data/lib/text2048/app.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'text2048'
|
4
|
+
require 'timeout'
|
4
5
|
|
5
6
|
# This module smells of :reek:UncommunicativeModuleName
|
6
7
|
module Text2048
|
@@ -9,9 +10,23 @@ module Text2048
|
|
9
10
|
attr_reader :board
|
10
11
|
attr_reader :view
|
11
12
|
|
12
|
-
def initialize(view = CursesView.new,
|
13
|
+
def initialize(view = CursesView.new,
|
14
|
+
board = Board.new,
|
15
|
+
high_score = HighScore.new)
|
13
16
|
@view = view
|
14
17
|
@board = board
|
18
|
+
@high_score = high_score
|
19
|
+
end
|
20
|
+
|
21
|
+
def show_title
|
22
|
+
@view.high_score(@high_score)
|
23
|
+
@view.message = 'PRESS ANY KEY TO START'
|
24
|
+
@board = Board.new([[0, 0, 0, 0],
|
25
|
+
[2, 0, 4, 8],
|
26
|
+
[0, 0, 0, 0],
|
27
|
+
[0, 0, 0, 0]])
|
28
|
+
@view.update(@board)
|
29
|
+
@view.wait_any_key
|
15
30
|
end
|
16
31
|
|
17
32
|
def generate(num_tiles = 1)
|
@@ -24,6 +39,20 @@ module Text2048
|
|
24
39
|
@view.win if @board.win?
|
25
40
|
@view.game_over if @board.lose?
|
26
41
|
input @view.command
|
42
|
+
@view.high_score(@high_score)
|
43
|
+
end
|
44
|
+
|
45
|
+
def wait_any_key(seconds)
|
46
|
+
begin
|
47
|
+
timeout(seconds) { @view.wait_any_key }
|
48
|
+
rescue Timeout::Error
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def demo
|
55
|
+
input [:left, :right, :up, :down][rand(5)]
|
27
56
|
end
|
28
57
|
|
29
58
|
private
|
@@ -42,6 +71,7 @@ module Text2048
|
|
42
71
|
def move_and_generate(command)
|
43
72
|
last = move(command)
|
44
73
|
generate if @board.generate?(last)
|
74
|
+
@high_score.maybe_update(@board.score)
|
45
75
|
end
|
46
76
|
|
47
77
|
def move(command)
|
data/lib/text2048/curses_view.rb
CHANGED
@@ -20,20 +20,25 @@ module Text2048
|
|
20
20
|
DEFAULT_WIDTH = (Tile::DEFAULT_WIDTH + 1) * 4 + 1
|
21
21
|
DEFAULT_HEIGHT = (Tile::DEFAULT_HEIGHT + 1) * 4 + 2
|
22
22
|
|
23
|
+
attr_writer :message
|
24
|
+
|
23
25
|
def initialize
|
24
26
|
@tiles = {}
|
25
27
|
@scale = 1
|
26
28
|
@scale_min = 0.5
|
27
29
|
@scale_step = 0.5
|
30
|
+
@message = nil
|
28
31
|
@keyboard = Keyboard.new
|
29
32
|
end
|
30
33
|
|
31
34
|
def_delegator :@keyboard, :read, :command
|
35
|
+
def_delegators :@keyboard, :wait_any_key
|
32
36
|
|
33
37
|
def update(board)
|
34
38
|
maybe_init_curses
|
35
39
|
draw_score(board.score)
|
36
40
|
draw_tiles(board.to_a)
|
41
|
+
draw_message
|
37
42
|
refresh
|
38
43
|
end
|
39
44
|
|
@@ -58,13 +63,25 @@ module Text2048
|
|
58
63
|
end
|
59
64
|
|
60
65
|
def win
|
61
|
-
|
62
|
-
|
66
|
+
@message = 'WIN!'
|
67
|
+
draw_message
|
63
68
|
end
|
64
69
|
|
65
70
|
def game_over
|
66
|
-
|
67
|
-
|
71
|
+
@message = 'GAME OVER'
|
72
|
+
draw_message
|
73
|
+
end
|
74
|
+
|
75
|
+
def high_score(score)
|
76
|
+
maybe_init_curses
|
77
|
+
setpos(0, 15)
|
78
|
+
addstr("High Score: #{score.to_i}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def draw_message
|
82
|
+
return unless @message
|
83
|
+
setpos(rows_center, cols_center - @message.length / 2)
|
84
|
+
colorize(COLOR_MAGENTA) { addstr(@message) }
|
68
85
|
end
|
69
86
|
|
70
87
|
private
|
@@ -77,7 +94,7 @@ module Text2048
|
|
77
94
|
end
|
78
95
|
|
79
96
|
def rows_center
|
80
|
-
height / 2
|
97
|
+
height / 2 + 1
|
81
98
|
end
|
82
99
|
|
83
100
|
def cols_center
|
@@ -6,32 +6,34 @@ module Text2048
|
|
6
6
|
# Renders numbers like LCDs
|
7
7
|
class LCD
|
8
8
|
BITMAPS = [
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
0b111111110101011,
|
10
|
+
0b101010010001000,
|
11
|
+
0b111111100111001,
|
12
|
+
0b111111110011001,
|
13
|
+
0b101111010011010,
|
14
|
+
0b111111110010011,
|
15
|
+
0b111111110110011,
|
16
|
+
0b101011010001001,
|
17
|
+
0b111111110111011,
|
18
|
+
0b111111110011011
|
19
19
|
]
|
20
20
|
|
21
21
|
TOP = 0
|
22
22
|
TOP_LEFT = 1
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
TOP_MIDDLE = 2
|
24
|
+
TOP_RIGHT = 3
|
25
|
+
MIDDLE = 4
|
26
|
+
BOTTOM_LEFT = 5
|
27
|
+
BOTTOM_MIDDLE = 6
|
28
|
+
BOTTOM_RIGHT = 7
|
29
|
+
BOTTOM = 8
|
28
30
|
|
29
|
-
CORNER_TOP_LEFT =
|
30
|
-
CORNER_TOP_RIGHT =
|
31
|
-
CORNER_MIDDLE_LEFT =
|
32
|
-
CORNER_MIDDLE_RIGHT =
|
33
|
-
CORNER_BOTTOM_LEFT =
|
34
|
-
CORNER_BOTTOM_RIGHT =
|
31
|
+
CORNER_TOP_LEFT = 9
|
32
|
+
CORNER_TOP_RIGHT = 10
|
33
|
+
CORNER_MIDDLE_LEFT = 11
|
34
|
+
CORNER_MIDDLE_RIGHT = 12
|
35
|
+
CORNER_BOTTOM_LEFT = 13
|
36
|
+
CORNER_BOTTOM_RIGHT = 14
|
35
37
|
|
36
38
|
def initialize(number)
|
37
39
|
@number = number
|
@@ -47,9 +49,9 @@ module Text2048
|
|
47
49
|
def digit(number)
|
48
50
|
lines =
|
49
51
|
[[CORNER_TOP_LEFT, TOP, CORNER_TOP_RIGHT],
|
50
|
-
[TOP_LEFT,
|
52
|
+
[TOP_LEFT, TOP_MIDDLE, TOP_RIGHT],
|
51
53
|
[CORNER_MIDDLE_LEFT, MIDDLE, CORNER_MIDDLE_RIGHT],
|
52
|
-
[BOTTOM_LEFT,
|
54
|
+
[BOTTOM_LEFT, BOTTOM_MIDDLE, BOTTOM_RIGHT],
|
53
55
|
[CORNER_BOTTOM_LEFT, BOTTOM, CORNER_BOTTOM_RIGHT]]
|
54
56
|
lines.map { |each| line(BITMAPS[number.to_i], each) }
|
55
57
|
end
|
@@ -57,13 +59,7 @@ module Text2048
|
|
57
59
|
# @todo This method smells of :reek:UtilityFunction
|
58
60
|
# @todo This method smells of :reek:FeatureEnvy
|
59
61
|
def line(bitmap, bits)
|
60
|
-
bits.map
|
61
|
-
if each
|
62
|
-
(bitmap & 1 << each).zero? ? ' ' : '*'
|
63
|
-
else
|
64
|
-
' '
|
65
|
-
end
|
66
|
-
end.join
|
62
|
+
bits.map { |each| (bitmap & 1 << each).zero? ? ' ' : '*' }.join
|
67
63
|
end
|
68
64
|
end
|
69
65
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This module smells of :reek:UncommunicativeModuleName
|
4
|
+
module Text2048
|
5
|
+
# High score manager
|
6
|
+
class HighScore
|
7
|
+
DB_FILE = File.expand_path('~/.text2048')
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@score = 0
|
11
|
+
load
|
12
|
+
end
|
13
|
+
|
14
|
+
def load
|
15
|
+
@score = IO.read(DB_FILE).to_i if FileTest.exists?(DB_FILE)
|
16
|
+
@score
|
17
|
+
end
|
18
|
+
|
19
|
+
def maybe_update(score)
|
20
|
+
load
|
21
|
+
save(score) if score > @score
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_i
|
25
|
+
@score
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def save(score)
|
31
|
+
File.open(DB_FILE, 'w') { |file| file.print score }
|
32
|
+
@score = score
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/text2048/version.rb
CHANGED
data/spec/text2048/app_spec.rb
CHANGED
@@ -17,7 +17,9 @@ describe Text2048::App do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
describe '#generate' do
|
20
|
-
Given(:view)
|
20
|
+
Given(:view) do
|
21
|
+
double('view', update: nil, zoom_tiles: nil, high_score: nil)
|
22
|
+
end
|
21
23
|
Given(:app) { Text2048::App.new(view) }
|
22
24
|
|
23
25
|
context 'with no arguments' do
|
@@ -33,18 +35,22 @@ describe Text2048::App do
|
|
33
35
|
|
34
36
|
describe '#step' do
|
35
37
|
Given(:new_board) do
|
36
|
-
double('new_board', merged_tiles: nil, generate?: nil)
|
38
|
+
double('new_board', merged_tiles: nil, generate?: nil, score: 0)
|
37
39
|
end
|
38
40
|
|
39
41
|
context 'with command = :larger' do
|
40
|
-
Given(:view)
|
42
|
+
Given(:view) do
|
43
|
+
double('view', command: :larger, larger: nil, high_score: nil)
|
44
|
+
end
|
41
45
|
Given(:app) { Text2048::App.new(view) }
|
42
46
|
When { app.step }
|
43
47
|
Then { expect(view).to have_received(:larger) }
|
44
48
|
end
|
45
49
|
|
46
50
|
context 'with command = :smaller' do
|
47
|
-
Given(:view)
|
51
|
+
Given(:view) do
|
52
|
+
double('view', command: :smaller, smaller: nil, high_score: nil)
|
53
|
+
end
|
48
54
|
Given(:app) { Text2048::App.new(view) }
|
49
55
|
When { app.step }
|
50
56
|
Then { expect(view).to have_received(:smaller) }
|
@@ -52,10 +58,12 @@ describe Text2048::App do
|
|
52
58
|
|
53
59
|
context 'with command = :left' do
|
54
60
|
Given(:view) do
|
55
|
-
double('view',
|
61
|
+
double('view',
|
62
|
+
command: :left, update: nil, pop_tiles: nil, high_score: nil)
|
56
63
|
end
|
57
64
|
Given(:board) { double('board', left: new_board, win?: nil, lose?: nil) }
|
58
|
-
Given(:
|
65
|
+
Given(:high_score) { double('high_score', maybe_update: nil) }
|
66
|
+
Given(:app) { Text2048::App.new(view, board, high_score) }
|
59
67
|
When { app.step }
|
60
68
|
Then { expect(board).to have_received(:left) }
|
61
69
|
And { expect(view).to have_received(:update) }
|
@@ -63,12 +71,14 @@ describe Text2048::App do
|
|
63
71
|
|
64
72
|
context 'with command = :right' do
|
65
73
|
Given(:view) do
|
66
|
-
double('view',
|
74
|
+
double('view',
|
75
|
+
command: :right, update: nil, pop_tiles: nil, high_score: nil)
|
67
76
|
end
|
68
77
|
Given(:board) do
|
69
78
|
double('board', right: new_board, win?: nil, lose?: nil)
|
70
79
|
end
|
71
|
-
Given(:
|
80
|
+
Given(:high_score) { double('high_score', maybe_update: nil) }
|
81
|
+
Given(:app) { Text2048::App.new(view, board, high_score) }
|
72
82
|
When { app.step }
|
73
83
|
Then { expect(board).to have_received(:right) }
|
74
84
|
And { expect(view).to have_received(:update) }
|
@@ -76,10 +86,12 @@ describe Text2048::App do
|
|
76
86
|
|
77
87
|
context 'with command = :up' do
|
78
88
|
Given(:view) do
|
79
|
-
double('view',
|
89
|
+
double('view',
|
90
|
+
command: :up, update: nil, pop_tiles: nil, high_score: nil)
|
80
91
|
end
|
81
92
|
Given(:board) { double('board', up: new_board, win?: nil, lose?: nil) }
|
82
|
-
Given(:
|
93
|
+
Given(:high_score) { double('high_score', maybe_update: nil) }
|
94
|
+
Given(:app) { Text2048::App.new(view, board, high_score) }
|
83
95
|
When { app.step }
|
84
96
|
Then { expect(board).to have_received(:up) }
|
85
97
|
And { expect(view).to have_received(:update) }
|
@@ -87,10 +99,12 @@ describe Text2048::App do
|
|
87
99
|
|
88
100
|
context 'with command = :down' do
|
89
101
|
Given(:view) do
|
90
|
-
double('view',
|
102
|
+
double('view',
|
103
|
+
command: :down, update: nil, pop_tiles: nil, high_score: nil)
|
91
104
|
end
|
92
105
|
Given(:board) { double('board', down: new_board, win?: nil, lose?: nil) }
|
93
|
-
Given(:
|
106
|
+
Given(:high_score) { double('high_score', maybe_update: nil) }
|
107
|
+
Given(:app) { Text2048::App.new(view, board, high_score) }
|
94
108
|
When { app.step }
|
95
109
|
Then { expect(board).to have_received(:down) }
|
96
110
|
And { expect(view).to have_received(:update) }
|
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.9.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-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/text2048/curses_view/lcd.rb
|
57
57
|
- lib/text2048/curses_view/tile.rb
|
58
58
|
- lib/text2048/curses_view/tile_effects.rb
|
59
|
+
- lib/text2048/high_score.rb
|
59
60
|
- lib/text2048/monkey_patch/array.rb
|
60
61
|
- lib/text2048/monkey_patch/array/board.rb
|
61
62
|
- lib/text2048/monkey_patch/array/tile.rb
|