rb2048 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +26 -0
- data/LICENSE.txt +21 -0
- data/README.md +55 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/rb2048 +45 -0
- data/lib/rb2048/game.rb +212 -0
- data/lib/rb2048/game_board.rb +291 -0
- data/lib/rb2048/logger.rb +21 -0
- data/lib/rb2048/version.rb +5 -0
- data/lib/rb2048.rb +12 -0
- data/sig/rb2048.rbs +4 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bfd2e7bd56a12fbf552193b69014f17be9d094cadc5c2b31932693697d0a5bc9
|
4
|
+
data.tar.gz: d2558a8b1c94f5e98c66ccca187e0172deca3db52e72e514afdc6ae79deae21c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3054b8b701af938df0200407eac694447aa8526b828d93aa3bfee2f908a5e34d8657b48003eeed74cadad0b144e5b1a1a99ffa9b45c738d50150e10eea08fe00
|
7
|
+
data.tar.gz: 43215747406b1805eada7ccff358ce3495ff3343011f16990b10c1e5e4e30c61fcab834e0fc51643dbad26553253069612fa85adae83c5a406884b358495c209
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rb2048 (1.0.0)
|
5
|
+
curses (~> 1.4.4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
curses (1.4.4)
|
11
|
+
minitest (5.16.2)
|
12
|
+
rake (13.0.6)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
arm64-darwin-21
|
16
|
+
ruby
|
17
|
+
x86_64-darwin
|
18
|
+
x86_64-linux
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
minitest (~> 5.0)
|
22
|
+
rake (~> 13.0)
|
23
|
+
rb2048!
|
24
|
+
|
25
|
+
BUNDLED WITH
|
26
|
+
2.3.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Mark24
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Rb2048
|
2
|
+
|
3
|
+
Ruby 2048 Game
|
4
|
+
|
5
|
+
|
6
|
+
## Install
|
7
|
+
|
8
|
+
`gem install rb2048`
|
9
|
+
|
10
|
+
## Help
|
11
|
+
|
12
|
+
```
|
13
|
+
Usage: rb2048 [options]
|
14
|
+
--version verison
|
15
|
+
--size SIZE Size of board: 4-10
|
16
|
+
--level LEVEL Hard Level 2-5
|
17
|
+
```
|
18
|
+
## Game Screen
|
19
|
+
|
20
|
+
```
|
21
|
+
|
22
|
+
-- Ruby 2048 --
|
23
|
+
|
24
|
+
-------------------------------------
|
25
|
+
| 16 | 16 | 2 | 16 |
|
26
|
+
-------------------------------------
|
27
|
+
| 0 | 0 | 0 | 0 |
|
28
|
+
-------------------------------------
|
29
|
+
| 0 | 0 | 0 | 2 |
|
30
|
+
-------------------------------------
|
31
|
+
| 0 | 0 | 0 | 0 |
|
32
|
+
-------------------------------------
|
33
|
+
|
34
|
+
Score: 16 You:UP
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
Control: W(↑) A(←) S(↓) D(→) Q(quit) R(Restart)
|
39
|
+
```
|
40
|
+
|
41
|
+
## Game Model Lightspot
|
42
|
+
|
43
|
+
Not just a main thread to create TUI Game.
|
44
|
+
|
45
|
+
This is a experiment for just testing Ruby3 Thread & Queue.
|
46
|
+
|
47
|
+
I build this game that UserI/O, Data Computing, TUI Render use 3 different threads & message channel to work together.
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Mark24Code/rb2048.
|
52
|
+
|
53
|
+
## License
|
54
|
+
|
55
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "rb2048"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/rb2048
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require_relative "../lib/rb2048"
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = "Usage: rb2048 [options]"
|
8
|
+
|
9
|
+
opts.on("--version", "verison") do |v|
|
10
|
+
# optins[:verison] = v
|
11
|
+
puts "Ruby2048: v#{::Rb2048::VERSION}"
|
12
|
+
puts "author: Mark24"
|
13
|
+
puts "mail: mark.zhangyoung@qq.com"
|
14
|
+
puts "project: https://github.com/Mark24Code/rb2048"
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("--size SIZE", Integer, "Size of board: 4-10") do |size|
|
19
|
+
if size < 4
|
20
|
+
puts "--size too small,it should in [4,10].Then will use size=4 as default"
|
21
|
+
sleep 1
|
22
|
+
elsif size > 10
|
23
|
+
puts "--size too small,it should in [4,10].It will use size=10."
|
24
|
+
sleep 1
|
25
|
+
end
|
26
|
+
options[:size] = size
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("--level LEVEL", Integer, "Hard Level 2-5") do |level|
|
30
|
+
if level < 2
|
31
|
+
puts "--level too small,it should in [2,5].Then will use level=2 as default"
|
32
|
+
sleep 1
|
33
|
+
elsif level > 5
|
34
|
+
puts "--level too small,it should in [2,5].It will use size=5."
|
35
|
+
sleep 1
|
36
|
+
end
|
37
|
+
options[:level] = level
|
38
|
+
end
|
39
|
+
|
40
|
+
end.parse!
|
41
|
+
|
42
|
+
init_size = options[:size] || 4
|
43
|
+
init_level = options[:level] || 2
|
44
|
+
|
45
|
+
::Rb2048::Game.new(nil,init_size,init_level).run
|
data/lib/rb2048/game.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require "curses"
|
2
|
+
require_relative './game_board'
|
3
|
+
require "thread"
|
4
|
+
require_relative './logger'
|
5
|
+
|
6
|
+
|
7
|
+
trap("INT") { ::Rb2048::Game.quit_game; exit 0 }
|
8
|
+
|
9
|
+
module Rb2048
|
10
|
+
class Game
|
11
|
+
|
12
|
+
def self.quit_game
|
13
|
+
Curses.close_screen
|
14
|
+
printf "Goodbye :P \n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(init_elements = nil, size=4, level=2)
|
18
|
+
@logger = LoggerMan
|
19
|
+
|
20
|
+
@level = level
|
21
|
+
@size = size
|
22
|
+
|
23
|
+
@render = ::Rb2048::GameRender.new
|
24
|
+
@window = @render.window
|
25
|
+
|
26
|
+
@frame_data_channel = Queue.new
|
27
|
+
@refresh_rate = 60
|
28
|
+
@frame_delta = 1.0 / @refresh_rate
|
29
|
+
|
30
|
+
@io_data_channel = Queue.new
|
31
|
+
|
32
|
+
init_game(init_elements)
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
init_game
|
37
|
+
main_loop
|
38
|
+
end
|
39
|
+
|
40
|
+
def init_game(init_elements=nil)
|
41
|
+
@backend = ::Rb2048::GameBoard.new(@size,@level)
|
42
|
+
@backend.create_elements(init_elements)
|
43
|
+
|
44
|
+
@logger.log("INIT", "start")
|
45
|
+
@frame_data_channel << @backend.tun_result
|
46
|
+
end
|
47
|
+
|
48
|
+
def main_loop
|
49
|
+
key_listener_thr = Thread.new { self.key_listener }
|
50
|
+
action_thr = Thread.new { self.action }
|
51
|
+
render_thr = Thread.new { self.render }
|
52
|
+
|
53
|
+
[key_listener_thr,action_thr, render_thr].map(&:join)
|
54
|
+
end
|
55
|
+
|
56
|
+
def action
|
57
|
+
while (command = @io_data_channel.shift)
|
58
|
+
if command == :QUIT
|
59
|
+
self.class.quit_game
|
60
|
+
exit 0
|
61
|
+
elsif command == :RESTART
|
62
|
+
self.init_game
|
63
|
+
else
|
64
|
+
@backend.__send__("#{command.to_s.downcase}_merge")
|
65
|
+
frame_data = @backend.tun_result
|
66
|
+
@frame_data_channel << frame_data
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def key_listener()
|
73
|
+
while true
|
74
|
+
command = nil
|
75
|
+
|
76
|
+
k = @window.getch
|
77
|
+
if k == Curses::KEY_UP || k == "w"
|
78
|
+
command = :UP
|
79
|
+
elsif k == Curses::KEY_DOWN || k == "s"
|
80
|
+
command = :DOWN
|
81
|
+
elsif k == Curses::KEY_LEFT || k == "a"
|
82
|
+
command = :LEFT
|
83
|
+
elsif k == Curses::KEY_RIGHT || k == "d"
|
84
|
+
command = :RIGHT
|
85
|
+
elsif k == "q"
|
86
|
+
command = :QUIT
|
87
|
+
elsif k == "r"
|
88
|
+
command = :RESTART
|
89
|
+
else
|
90
|
+
end
|
91
|
+
|
92
|
+
@io_data_channel << command if command
|
93
|
+
sleep 0.08
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def render
|
98
|
+
while (frame_data = @frame_data_channel.shift)
|
99
|
+
break if frame_data == :done
|
100
|
+
@render.draw(frame_data) if frame_data
|
101
|
+
sleep 0.16
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class GameRender
|
107
|
+
attr :window
|
108
|
+
def initialize
|
109
|
+
@window = nil
|
110
|
+
@window_height = nil
|
111
|
+
@window_width = nil
|
112
|
+
|
113
|
+
init_screen
|
114
|
+
end
|
115
|
+
|
116
|
+
def init_screen
|
117
|
+
Curses.init_screen
|
118
|
+
Curses.cbreak
|
119
|
+
Curses.noecho
|
120
|
+
Curses.curs_set(0) # Invisible cursor
|
121
|
+
Curses.stdscr.keypad = true
|
122
|
+
|
123
|
+
@window_height = Curses.lines
|
124
|
+
@window_width = Curses.cols
|
125
|
+
|
126
|
+
@window = Curses::Window.new(@window_height, @window_width, 0, 0)
|
127
|
+
@window.refresh
|
128
|
+
end
|
129
|
+
|
130
|
+
def draw(payload)
|
131
|
+
data = payload[:data]
|
132
|
+
size = payload[:size]
|
133
|
+
|
134
|
+
level = payload[:level]
|
135
|
+
score = payload[:score]
|
136
|
+
last_action = payload[:last_action]
|
137
|
+
status = payload[:status]
|
138
|
+
|
139
|
+
start_timestamp = payload[:start_timestamp]
|
140
|
+
end_timestamp = payload[:end_timestamp]
|
141
|
+
|
142
|
+
tun_new_value = payload[:tun_new_value]
|
143
|
+
|
144
|
+
@window.clear
|
145
|
+
|
146
|
+
# display board content buffer
|
147
|
+
buffer = []
|
148
|
+
number_width = score.to_s.length * 2
|
149
|
+
for row_index in (0..size-1)
|
150
|
+
row_text = "|"
|
151
|
+
for col_index in (0..size-1)
|
152
|
+
|
153
|
+
n = data[row_index][col_index]
|
154
|
+
n_text = " "*(number_width - n.to_s.length) + n.to_s
|
155
|
+
row_text +=" "*2+"#{n_text}"+" "*2+"|"
|
156
|
+
end
|
157
|
+
buffer.push(row_text)
|
158
|
+
end
|
159
|
+
|
160
|
+
# x,y offset counter
|
161
|
+
row,col = [0,1]
|
162
|
+
|
163
|
+
# title
|
164
|
+
content_width = buffer.first.length
|
165
|
+
title = "-- Ruby 2048 --"
|
166
|
+
left_offset = (content_width - title.length) / 2
|
167
|
+
row = row + 1
|
168
|
+
@window.setpos(row, left_offset)
|
169
|
+
@window.addstr("-- Ruby 2048 --")
|
170
|
+
|
171
|
+
# board
|
172
|
+
row = row + 2
|
173
|
+
start_row, start_col = [row,1]
|
174
|
+
@window.setpos(start_row, start_col)
|
175
|
+
@window.addstr("-"*buffer.first.length)
|
176
|
+
buffer.each_with_index do |line,index|
|
177
|
+
@window.setpos(start_row+index*2 + 2, start_col)
|
178
|
+
@window.addstr("-" * line.length)
|
179
|
+
@window.setpos(start_row+index*2 + 1, start_col)
|
180
|
+
@window.addstr(line)
|
181
|
+
end
|
182
|
+
|
183
|
+
# score
|
184
|
+
row = row + buffer.length * 2 + 2
|
185
|
+
@window.setpos(row, col)
|
186
|
+
@window.addstr("Score:\t#{score}\t\t#{"You:"+last_action.to_s if last_action}")
|
187
|
+
|
188
|
+
# status
|
189
|
+
|
190
|
+
row = row + 2
|
191
|
+
@window.setpos(row, col)
|
192
|
+
result_text = nil
|
193
|
+
if status == 1
|
194
|
+
result_text = "Congratulation! You have got 2048!!! :D"
|
195
|
+
elsif status == -1
|
196
|
+
result_text = "YOU LOST :("
|
197
|
+
end
|
198
|
+
|
199
|
+
if result_text
|
200
|
+
left_offset = (buffer.first.length - result_text.length )/2
|
201
|
+
@window.setpos(row, left_offset)
|
202
|
+
@window.addstr(result_text)
|
203
|
+
end
|
204
|
+
|
205
|
+
# control
|
206
|
+
row = row + 2
|
207
|
+
@window.setpos(row, col)
|
208
|
+
@window.addstr("Control: W(↑) A(←) S(↓) D(→) Q(quit) R(Restart)")
|
209
|
+
@window.refresh
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
require_relative './logger'
|
2
|
+
|
3
|
+
module Rb2048
|
4
|
+
class GameBoard
|
5
|
+
attr :elements, :status
|
6
|
+
def initialize(size=4, level = 2,win_standard=2048)
|
7
|
+
@size = size
|
8
|
+
@elements = []
|
9
|
+
|
10
|
+
@level = level
|
11
|
+
@score = 0
|
12
|
+
@start_timestamp = nil
|
13
|
+
@end_timestamp = nil
|
14
|
+
@status = 0 # -1 lost, 0 init/running, 1 win
|
15
|
+
@win_standard = win_standard
|
16
|
+
|
17
|
+
@last_action = nil
|
18
|
+
@logger = LoggerMan
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_init_value
|
22
|
+
total_count = @size ** 2
|
23
|
+
init_value_count = total_count * @level / 10
|
24
|
+
zero_value_count = total_count - init_value_count
|
25
|
+
|
26
|
+
values = []
|
27
|
+
for i in (0..init_value_count-1)
|
28
|
+
values.push(rand_init)
|
29
|
+
end
|
30
|
+
|
31
|
+
for i in (0..zero_value_count-1)
|
32
|
+
values.push(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
values.shuffle
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_elements(init_elements = nil)
|
39
|
+
pos_values = init_elements || create_init_value
|
40
|
+
|
41
|
+
max_nums = @size - 1
|
42
|
+
for i in (0..max_nums)
|
43
|
+
row = []
|
44
|
+
for j in (0..max_nums)
|
45
|
+
row.push(pos_values[i*@size+j])
|
46
|
+
end
|
47
|
+
@elements.push(row)
|
48
|
+
end
|
49
|
+
@elements
|
50
|
+
end
|
51
|
+
|
52
|
+
def bigger_then_zero(arr)
|
53
|
+
arr.filter { |e| e > 0}
|
54
|
+
end
|
55
|
+
|
56
|
+
def left_merge
|
57
|
+
@logger.log("Left", @elements)
|
58
|
+
for row_index in (0..@size-1)
|
59
|
+
row = @elements[row_index]
|
60
|
+
update_row = update_line(row)
|
61
|
+
@elements[row_index] = update_row
|
62
|
+
end
|
63
|
+
@last_action = :LEFT
|
64
|
+
@elements
|
65
|
+
end
|
66
|
+
|
67
|
+
def right_merge
|
68
|
+
@logger.log("Right", @elements)
|
69
|
+
for row_index in (0..@size-1)
|
70
|
+
row = @elements[row_index]
|
71
|
+
update_row = update_line(row.reverse)
|
72
|
+
@elements[row_index] = update_row.reverse
|
73
|
+
end
|
74
|
+
@last_action = :RIGHT
|
75
|
+
@elements
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def up_merge
|
80
|
+
@logger.log("UP", @elements)
|
81
|
+
for col_index in (0..@size-1)
|
82
|
+
col = []
|
83
|
+
for row_index in (0..@size-1)
|
84
|
+
col.push(@elements[row_index][col_index])
|
85
|
+
end
|
86
|
+
update_col = update_line(col)
|
87
|
+
|
88
|
+
for row_index in (0..@size-1)
|
89
|
+
@elements[row_index][col_index] = update_col[row_index]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
@last_action = :UP
|
93
|
+
@elements
|
94
|
+
end
|
95
|
+
|
96
|
+
def down_merge
|
97
|
+
@logger.log("Down", @elements)
|
98
|
+
for col_index in (0..@size-1)
|
99
|
+
col = []
|
100
|
+
for row_index in (0..@size-1)
|
101
|
+
col.push(@elements[row_index][col_index])
|
102
|
+
end
|
103
|
+
|
104
|
+
update_col = update_line(col.reverse).reverse
|
105
|
+
# update
|
106
|
+
for row_index in (0..@size-1)
|
107
|
+
@elements[row_index][col_index] = update_col[row_index]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
@last_action = :DOWN
|
111
|
+
@elements
|
112
|
+
end
|
113
|
+
|
114
|
+
def merge_once(arr)
|
115
|
+
|
116
|
+
if arr.length <= 1
|
117
|
+
return arr
|
118
|
+
end
|
119
|
+
|
120
|
+
data = arr.clone
|
121
|
+
result = []
|
122
|
+
first = nil
|
123
|
+
next_ = nil
|
124
|
+
|
125
|
+
while !data.empty?
|
126
|
+
if first == nil
|
127
|
+
first = data.shift
|
128
|
+
end
|
129
|
+
|
130
|
+
if next_ == nil
|
131
|
+
next_ = data.shift
|
132
|
+
end
|
133
|
+
|
134
|
+
if first != next_
|
135
|
+
result.push(first)
|
136
|
+
if next_
|
137
|
+
data.unshift(next_)
|
138
|
+
end
|
139
|
+
first = nil
|
140
|
+
next_ = nil
|
141
|
+
else
|
142
|
+
result.push(first + next_)
|
143
|
+
|
144
|
+
first = nil
|
145
|
+
next_ = nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
return result
|
150
|
+
end
|
151
|
+
|
152
|
+
def collect_max_score(result)
|
153
|
+
max_ = result.max
|
154
|
+
if max_
|
155
|
+
@score = @score > result.max ? @score : result.max
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def merge(arr)
|
160
|
+
if arr.length <= 0
|
161
|
+
return arr
|
162
|
+
end
|
163
|
+
result = arr.clone
|
164
|
+
|
165
|
+
while true
|
166
|
+
before = result.clone
|
167
|
+
result = merge_once(result)
|
168
|
+
break if before == result
|
169
|
+
end
|
170
|
+
|
171
|
+
return result
|
172
|
+
end
|
173
|
+
|
174
|
+
def create_zero_array(len)
|
175
|
+
Array.new(len,0)
|
176
|
+
end
|
177
|
+
|
178
|
+
def update_line(arr)
|
179
|
+
size = arr.length
|
180
|
+
compact = merge(bigger_then_zero(arr))
|
181
|
+
collect_max_score(compact)
|
182
|
+
compact.concat(create_zero_array(size - compact.length))
|
183
|
+
end
|
184
|
+
|
185
|
+
def start_timer
|
186
|
+
@start_timestamp = Time.now.to_i
|
187
|
+
end
|
188
|
+
|
189
|
+
def close_timer
|
190
|
+
@end_timestamp = Time.now.to_i
|
191
|
+
end
|
192
|
+
|
193
|
+
def check_dead_road
|
194
|
+
dead_road = @size * 2
|
195
|
+
|
196
|
+
for row_index in (0..@size-1)
|
197
|
+
row = @elements[row_index]
|
198
|
+
before = bigger_then_zero(row).clone
|
199
|
+
after = merge(before)
|
200
|
+
if before == after
|
201
|
+
dead_road -= 1
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
for col_index in (0..@size-1)
|
206
|
+
col = []
|
207
|
+
for row_index in (0..@size-1)
|
208
|
+
col.push(@elements[row_index][col_index])
|
209
|
+
end
|
210
|
+
|
211
|
+
before = bigger_then_zero(col).clone
|
212
|
+
after = merge(before)
|
213
|
+
if before == after
|
214
|
+
dead_road -= 1
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
dead_road
|
219
|
+
end
|
220
|
+
|
221
|
+
def check_game_status
|
222
|
+
if @score >= @win_standard
|
223
|
+
return @status = 1
|
224
|
+
else
|
225
|
+
if all_zero_pos.length == 0
|
226
|
+
dead_road = check_dead_road
|
227
|
+
if dead_road <= 0
|
228
|
+
@status = -1
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def rand_init
|
235
|
+
2 ** rand(1..4)
|
236
|
+
end
|
237
|
+
|
238
|
+
def create_new_number
|
239
|
+
zeros = all_zero_pos
|
240
|
+
if zeros.length > 0
|
241
|
+
x,y = zeros.sample
|
242
|
+
new_number = rand_init
|
243
|
+
@elements[x][y] = new_number
|
244
|
+
{pos:[x,y], value: new_number}
|
245
|
+
end
|
246
|
+
|
247
|
+
return nil
|
248
|
+
end
|
249
|
+
|
250
|
+
def all_zero_pos
|
251
|
+
zeros = []
|
252
|
+
for i in (0..@size-1)
|
253
|
+
for j in (0..@size-1)
|
254
|
+
e = @elements[i][j]
|
255
|
+
if e == 0
|
256
|
+
zeros.push([i,j])
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
zeros
|
262
|
+
end
|
263
|
+
|
264
|
+
def tun_result
|
265
|
+
tun_new_value = create_new_number
|
266
|
+
|
267
|
+
check_game_status
|
268
|
+
|
269
|
+
data = []
|
270
|
+
for i in (0..@elements.length-1)
|
271
|
+
row = []
|
272
|
+
for j in (0..@elements.length-1)
|
273
|
+
row.push(@elements[i][j])
|
274
|
+
end
|
275
|
+
data.push(row)
|
276
|
+
end
|
277
|
+
|
278
|
+
return {
|
279
|
+
data: data,
|
280
|
+
size: @size,
|
281
|
+
level: @level,
|
282
|
+
score: @score,
|
283
|
+
last_action: @last_action,
|
284
|
+
start_timestamp: @start_timestamp,
|
285
|
+
end_timestamp: @end_timestamp,
|
286
|
+
status: @status,
|
287
|
+
tun_new_value: tun_new_value
|
288
|
+
}
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Logger
|
2
|
+
def initialize(switch)
|
3
|
+
@switch = switch
|
4
|
+
if @switch == :on
|
5
|
+
@output = File.absolute_path("#{__dir__}/../../log")
|
6
|
+
@f = File.open("#{@output}/#{Time.now.to_s}.log","w+")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def log(component, info)
|
11
|
+
@f << "[#{component}]: #{Time.now.to_s}: #{info}\n" if @switch == :on
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
@f.close if @switch == :on
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
LoggerMan = Logger.new(:off)
|
20
|
+
|
21
|
+
at_exit { LoggerMan.close }
|
data/lib/rb2048.rb
ADDED
data/sig/rb2048.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rb2048
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark24
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: curses
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.4
|
27
|
+
description: 2048 command line game build by ruby.
|
28
|
+
email:
|
29
|
+
- mark.zhangyoung@qq.com
|
30
|
+
executables:
|
31
|
+
- rb2048
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- bin/console
|
41
|
+
- bin/setup
|
42
|
+
- exe/rb2048
|
43
|
+
- lib/rb2048.rb
|
44
|
+
- lib/rb2048/game.rb
|
45
|
+
- lib/rb2048/game_board.rb
|
46
|
+
- lib/rb2048/logger.rb
|
47
|
+
- lib/rb2048/version.rb
|
48
|
+
- sig/rb2048.rbs
|
49
|
+
homepage: https://github.com/Mark24Code/rb2048
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata:
|
53
|
+
homepage_uri: https://github.com/Mark24Code/rb2048
|
54
|
+
source_code_uri: https://github.com/Mark24Code/rb2048
|
55
|
+
changelog_uri: https://github.com/Mark24Code/rb2048
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 2.6.0
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubygems_version: 3.3.3
|
72
|
+
signing_key:
|
73
|
+
specification_version: 4
|
74
|
+
summary: Ruby Command line 2048 game
|
75
|
+
test_files: []
|