text2048 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ text2048
2
+ ========
3
+
4
+ Text mode 2048 game.
5
+
6
+ How to Play
7
+ ===========
8
+
9
+ ```
10
+ $ 2048
11
+ ```
12
+
13
+ - Use your arrow keys to move the tiles.
14
+ - +/- for increase or decrease the size of the tiles displayed.
15
+
16
+ [![Screenshot](https://raw.github.com/yasuhito/text2048/develop/screen_shot.png)][screenshot]
17
+ [screenshot]: https://raw.github.com/yasuhito/text2048/develop/screen_shot.png
18
+
19
+ Installation
20
+ ============
21
+
22
+ ```
23
+ $ gem install text2048
24
+ ```
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ task default: [:spec, :cucumber, :rubocop]
6
+
7
+ begin
8
+ require 'rspec/core/rake_task'
9
+ RSpec::Core::RakeTask.new
10
+ rescue LoadError
11
+ task :spec do
12
+ $stderr.puts 'RSpec is disabled'
13
+ end
14
+ end
15
+
16
+ begin
17
+ require 'cucumber/rake/task'
18
+ Cucumber::Rake::Task.new
19
+ rescue LoadError
20
+ task :cucumber do
21
+ $stderr.puts 'Cucumber is disabled'
22
+ end
23
+ end
24
+
25
+ begin
26
+ require 'rubocop/rake_task'
27
+ Rubocop::RakeTask.new
28
+ rescue LoadError
29
+ task :rubocop do
30
+ $stderr.puts 'Rubocop is disabled'
31
+ end
32
+ end
data/bin/2048 ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
4
+
5
+ require 'curses'
6
+ require 'text2048'
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
@@ -0,0 +1,72 @@
1
+ Feature: Move down
2
+ Scenario: Move empty board
3
+ Given a board:
4
+ """
5
+ _ _ _ _
6
+ _ _ _ _
7
+ _ _ _ _
8
+ _ _ _ _
9
+ """
10
+ When I move the board down
11
+ Then the board is:
12
+ """
13
+ _ _ _ _
14
+ _ _ _ _
15
+ _ _ _ _
16
+ _ _ _ _
17
+ """
18
+ And the score is 0
19
+
20
+ Scenario: Move numbers
21
+ Given a board:
22
+ """
23
+ 2 _ _ _
24
+ _ 2 _ _
25
+ _ _ 2 _
26
+ _ _ _ 2
27
+ """
28
+ When I move the board down
29
+ Then the board is:
30
+ """
31
+ _ _ _ _
32
+ _ _ _ _
33
+ _ _ _ _
34
+ 2 2 2 2
35
+ """
36
+ And the score is 0
37
+
38
+ Scenario: Merge numbers
39
+ Given a board:
40
+ """
41
+ 2 _ _ _
42
+ _ 2 2 _
43
+ 2 _ _ 2
44
+ _ _ 2 _
45
+ """
46
+ When I move the board down
47
+ Then the board is:
48
+ """
49
+ _ _ _ _
50
+ _ _ _ _
51
+ _ _ _ _
52
+ 4 2 4 2
53
+ """
54
+ And the score is 8
55
+
56
+ Scenario: Move and merge mix of numbers
57
+ Given a board:
58
+ """
59
+ 2 _ _ 2
60
+ _ 2 4 _
61
+ 4 _ _ _
62
+ _ _ 2 2
63
+ """
64
+ When I move the board down
65
+ Then the board is:
66
+ """
67
+ _ _ _ _
68
+ _ _ _ _
69
+ 2 _ 4 _
70
+ 4 2 2 4
71
+ """
72
+ And the score is 4
@@ -0,0 +1,72 @@
1
+ Feature: Move left
2
+ Scenario: Move empty board
3
+ Given a board:
4
+ """
5
+ _ _ _ _
6
+ _ _ _ _
7
+ _ _ _ _
8
+ _ _ _ _
9
+ """
10
+ When I move the board to the left
11
+ Then the board is:
12
+ """
13
+ _ _ _ _
14
+ _ _ _ _
15
+ _ _ _ _
16
+ _ _ _ _
17
+ """
18
+ And the score is 0
19
+
20
+ Scenario: Move numbers
21
+ Given a board:
22
+ """
23
+ _ _ _ 2
24
+ _ _ 2 _
25
+ _ 2 _ _
26
+ 2 _ _ _
27
+ """
28
+ When I move the board to the left
29
+ Then the board is:
30
+ """
31
+ 2 _ _ _
32
+ 2 _ _ _
33
+ 2 _ _ _
34
+ 2 _ _ _
35
+ """
36
+ And the score is 0
37
+
38
+ Scenario: Merge numbers
39
+ Given a board:
40
+ """
41
+ _ 2 _ 2
42
+ _ _ 2 _
43
+ 2 _ 2 _
44
+ _ 2 _ _
45
+ """
46
+ When I move the board to the left
47
+ Then the board is:
48
+ """
49
+ 4 _ _ _
50
+ 2 _ _ _
51
+ 4 _ _ _
52
+ 2 _ _ _
53
+ """
54
+ And the score is 8
55
+
56
+ Scenario: Move and merge mix of numbers
57
+ Given a board:
58
+ """
59
+ _ 4 _ 2
60
+ 2 _ 2 _
61
+ 2 _ 4 _
62
+ _ 2 _ _
63
+ """
64
+ When I move the board to the left
65
+ Then the board is:
66
+ """
67
+ 4 2 _ _
68
+ 4 _ _ _
69
+ 2 4 _ _
70
+ 2 _ _ _
71
+ """
72
+ And the score is 4
@@ -0,0 +1,72 @@
1
+ Feature: Move right
2
+ Scenario: Move empty board
3
+ Given a board:
4
+ """
5
+ _ _ _ _
6
+ _ _ _ _
7
+ _ _ _ _
8
+ _ _ _ _
9
+ """
10
+ When I move the board to the right
11
+ Then the board is:
12
+ """
13
+ _ _ _ _
14
+ _ _ _ _
15
+ _ _ _ _
16
+ _ _ _ _
17
+ """
18
+ And the score is 0
19
+
20
+ Scenario: Move numbers
21
+ Given a board:
22
+ """
23
+ 2 _ _ _
24
+ _ 2 _ _
25
+ _ _ 2 _
26
+ _ _ _ 2
27
+ """
28
+ When I move the board to the right
29
+ Then the board is:
30
+ """
31
+ _ _ _ 2
32
+ _ _ _ 2
33
+ _ _ _ 2
34
+ _ _ _ 2
35
+ """
36
+ And the score is 0
37
+
38
+ Scenario: Merge numbers
39
+ Given a board:
40
+ """
41
+ 2 _ 2 _
42
+ _ 2 _ _
43
+ _ 2 _ 2
44
+ _ _ _ 2
45
+ """
46
+ When I move the board to the right
47
+ Then the board is:
48
+ """
49
+ _ _ _ 4
50
+ _ _ _ 2
51
+ _ _ _ 4
52
+ _ _ _ 2
53
+ """
54
+ And the score is 8
55
+
56
+ Scenario: Move and merge mix of numbers
57
+ Given a board:
58
+ """
59
+ 4 _ 2 _
60
+ _ 2 _ 2
61
+ _ 2 _ 4
62
+ _ _ _ 2
63
+ """
64
+ When I move the board to the right
65
+ Then the board is:
66
+ """
67
+ _ _ 4 2
68
+ _ _ _ 4
69
+ _ _ 2 4
70
+ _ _ _ 2
71
+ """
72
+ And the score is 4
@@ -0,0 +1,72 @@
1
+ Feature: Move up
2
+ Scenario: Move empty board
3
+ Given a board:
4
+ """
5
+ _ _ _ _
6
+ _ _ _ _
7
+ _ _ _ _
8
+ _ _ _ _
9
+ """
10
+ When I move the board up
11
+ Then the board is:
12
+ """
13
+ _ _ _ _
14
+ _ _ _ _
15
+ _ _ _ _
16
+ _ _ _ _
17
+ """
18
+ And the score is 0
19
+
20
+ Scenario: Move numbers
21
+ Given a board:
22
+ """
23
+ 2 _ _ _
24
+ _ 2 _ _
25
+ _ _ 2 _
26
+ _ _ _ 2
27
+ """
28
+ When I move the board up
29
+ Then the board is:
30
+ """
31
+ 2 2 2 2
32
+ _ _ _ _
33
+ _ _ _ _
34
+ _ _ _ _
35
+ """
36
+ And the score is 0
37
+
38
+ Scenario: Merge numbers
39
+ Given a board:
40
+ """
41
+ 2 _ _ _
42
+ _ 2 2 _
43
+ 2 _ _ 2
44
+ _ _ 2 _
45
+ """
46
+ When I move the board up
47
+ Then the board is:
48
+ """
49
+ 4 2 4 2
50
+ _ _ _ _
51
+ _ _ _ _
52
+ _ _ _ _
53
+ """
54
+ And the score is 8
55
+
56
+ Scenario: Move and merge mix of numbers
57
+ Given a board:
58
+ """
59
+ 2 _ _ 2
60
+ _ 2 4 _
61
+ 4 _ _ _
62
+ _ _ 2 2
63
+ """
64
+ When I move the board up
65
+ Then the board is:
66
+ """
67
+ 2 2 4 4
68
+ 4 _ 2 _
69
+ _ _ _ _
70
+ _ _ _ _
71
+ """
72
+ And the score is 4
@@ -0,0 +1,33 @@
1
+ require 'text2048'
2
+
3
+ Given(/^a board:$/) do |string|
4
+ layout = string.split("\n").reduce([]) do |result, row|
5
+ result << row.split(' ').map(&:to_i)
6
+ end
7
+ @game = Text2048::Game.new(Text2048::TextView.new(output), layout)
8
+ end
9
+
10
+ When(/^I move the board to the right$/) do
11
+ @game.right!
12
+ end
13
+
14
+ When(/^I move the board to the left$/) do
15
+ @game.left!
16
+ end
17
+
18
+ When(/^I move the board up$/) do
19
+ @game.up!
20
+ end
21
+
22
+ When(/^I move the board down$/) do
23
+ @game.down!
24
+ end
25
+
26
+ Then(/^the board is:$/) do |string|
27
+ @game.draw
28
+ output.messages.should eq(string)
29
+ end
30
+
31
+ Then(/^the score is (\d+)$/) do |score|
32
+ @game.score.should eq(score.to_i)
33
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ # Dummy output
4
+ class Output
5
+ def messages
6
+ @messages ||= ''
7
+ end
8
+
9
+ def puts(message)
10
+ messages << message
11
+ end
12
+ end
13
+
14
+ def output
15
+ @output ||= Output.new
16
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+
3
+ require 'text2048/tile'
4
+ require 'text2048/tiles'
5
+
6
+ module Text2048
7
+ # Game board
8
+ class Board
9
+ attr_reader :tiles
10
+
11
+ def initialize(tiles = nil)
12
+ @tiles = Array.new(4) { Array.new(4) { Tile.new(0) } }
13
+ if tiles
14
+ load_tiles(tiles)
15
+ else
16
+ 2.times { generate }
17
+ end
18
+ end
19
+
20
+ def initialize_copy(board)
21
+ @tiles = board.tiles.dup
22
+ end
23
+
24
+ def layout
25
+ @tiles.map do |row|
26
+ row.map { |each| each.to_i }
27
+ end
28
+ end
29
+
30
+ def right!
31
+ move! :right
32
+ end
33
+
34
+ def left!
35
+ move! :left
36
+ end
37
+
38
+ def up!
39
+ transpose { move! :left }
40
+ end
41
+
42
+ def down!
43
+ transpose { move! :right }
44
+ end
45
+
46
+ def ==(other)
47
+ @tiles.zip(other.tiles).reduce(true) do |result, each|
48
+ result && Tiles.new(each[0]) == Tiles.new(each[1])
49
+ end
50
+ end
51
+
52
+ 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
60
+ end
61
+
62
+ 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
70
+ end
71
+
72
+ def generate
73
+ 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)
78
+ return
79
+ end
80
+ end
81
+ end
82
+
83
+ def numbers
84
+ tiles.reduce([]) do |result, row|
85
+ result + row.select { |each| each != 0 }
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def move!(direction)
92
+ score = 0
93
+ @tiles.map! do |each|
94
+ row, sc = Tiles.new(each).__send__ direction
95
+ score += sc
96
+ row
97
+ end
98
+ score
99
+ end
100
+
101
+ def transpose
102
+ @tiles = @tiles.transpose
103
+ score = yield
104
+ @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
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+
3
+ require 'curses'
4
+
5
+ module Text2048
6
+ # Shows tiles in curses.
7
+ class CursesTile
8
+ attr_reader :width
9
+ attr_reader :height
10
+ attr_reader :color
11
+
12
+ include Curses
13
+
14
+ DEFAULT_HEIGHT = 3
15
+ DEFAULT_WIDTH = 5
16
+
17
+ def initialize(value, y, x, color, scale = 1)
18
+ @value = value.to_i
19
+ @y = y
20
+ @x = x
21
+ @color = color
22
+ @height = (DEFAULT_HEIGHT * scale).to_i
23
+ @width = (DEFAULT_WIDTH * scale).to_i
24
+ end
25
+
26
+ def show
27
+ draw_box
28
+ attron(color_pair(@color + 100)) { fill }
29
+ attron(color_pair(@color)) { draw_number }
30
+ self
31
+ end
32
+
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('.')
42
+ end
43
+
44
+ setpos(yc + @height, xc - 1)
45
+ addstr('.' * (@width + 2))
46
+ end
47
+
48
+ def pop2
49
+ draw_box
50
+ refresh
51
+ end
52
+
53
+ def zoom1
54
+ attron(color_pair(COLOR_BLACK + 100)) { fill }
55
+ attron(color_pair(@color)) { draw_number }
56
+ refresh
57
+ end
58
+
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('...') }
66
+
67
+ attron(color_pair(@color)) { draw_number }
68
+
69
+ refresh
70
+ end
71
+
72
+ def zoom3
73
+ attron(color_pair(@color + 100)) { fill }
74
+ attron(color_pair(@color)) { draw_number }
75
+ refresh
76
+ end
77
+
78
+ private
79
+
80
+ def yc
81
+ (@height + 1) * @y + 2
82
+ end
83
+
84
+ def xc
85
+ (@width + 1) * @x + 1
86
+ end
87
+
88
+ def draw_box
89
+ setpos(yc - 1, xc - 1)
90
+ addstr("+#{'-' * @width}+")
91
+
92
+ (0..(@height - 1)).each do |dy|
93
+ setpos(yc + dy, xc - 1)
94
+ addstr('|')
95
+ setpos(yc + dy, xc + @width)
96
+ addstr('|')
97
+ end
98
+
99
+ setpos(yc + @height, xc - 1)
100
+ addstr("+#{'-' * @width}+")
101
+ end
102
+
103
+ def fill
104
+ (0..(@height - 1)).each do |dy|
105
+ setpos(yc + dy, xc)
106
+ if @value != 0 && dy == @height / 2
107
+ addstr @value.to_s.center(@width)
108
+ else
109
+ addstr('.' * @width)
110
+ end
111
+ end
112
+ 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
+ end
120
+ end