ruby_terminal_games 0.0.1
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 +7 -0
- data/README.md +32 -0
- data/bin/ruby_terminal_games +44 -0
- data/lib/ruby_terminal_games.rb +17 -0
- data/lib/ruby_terminal_games/board.rb +45 -0
- data/lib/ruby_terminal_games/game.rb +7 -0
- data/lib/ruby_terminal_games/keyboard.rb +34 -0
- data/lib/ruby_terminal_games/snake.rb +5 -0
- data/lib/ruby_terminal_games/snake/apple.rb +22 -0
- data/lib/ruby_terminal_games/snake/board.rb +63 -0
- data/lib/ruby_terminal_games/snake/game.rb +87 -0
- data/lib/ruby_terminal_games/snake/snake.rb +80 -0
- data/lib/ruby_terminal_games/version.rb +3 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e7fc58451957c43c34d3b0b50ce03ec8ce1ac778
|
4
|
+
data.tar.gz: 576e6a57b18cb64b30038afb63b1ad5578a0a48e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 12a135fd8c7e67f6c792356908ad875fd595df99662f67f6cbb672d78f467afe58eddc9c1883ab71ae1ed166b5eff093ccbac499fa5f8fd7bf7fa56a052eccf5
|
7
|
+
data.tar.gz: d9a8537ed71df86ce045aa40617c34e69d9ca17ae2a5ef37642d83be6a1364b1470e12e56e582ab4052916f7170a483ded3d9e8fabcfda3dc4a383761af2041d
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
## RubyTerminalGames
|
2
|
+
|
3
|
+
The idea of the gem is provide some basic games to play on Terminal. Currently the gem has the games below:
|
4
|
+
|
5
|
+
- [x] Snake
|
6
|
+
- [ ] Hangman
|
7
|
+
- Any suggestions?
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
$ gem install ruby_terminal_games
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```shell
|
16
|
+
$ ruby_terminal_games
|
17
|
+
Usage: ruby_terminal_games COMMAND [ARGS]
|
18
|
+
|
19
|
+
The most common commands are:
|
20
|
+
list Get the list of games
|
21
|
+
play Have fun!
|
22
|
+
about What is the game about?
|
23
|
+
```
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
I would love your contribution. Feel free to push some code!
|
28
|
+
|
29
|
+
|
30
|
+
## License
|
31
|
+
|
32
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/ruby_terminal_games'
|
3
|
+
command = ARGV.shift
|
4
|
+
arg = ARGV.shift
|
5
|
+
|
6
|
+
help = <<-EOT
|
7
|
+
Usage: ruby_terminal_games COMMAND [ARGS]
|
8
|
+
|
9
|
+
The most common commands are:
|
10
|
+
list Get the list of games
|
11
|
+
play Have fun!
|
12
|
+
about What is the game about?
|
13
|
+
EOT
|
14
|
+
|
15
|
+
if command =~ /list/
|
16
|
+
puts <<-EOT
|
17
|
+
Games:
|
18
|
+
- Snake (play snake)
|
19
|
+
EOT
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
|
23
|
+
unless command && arg
|
24
|
+
puts help
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
game = case arg.downcase
|
29
|
+
when /snake/
|
30
|
+
RubyTerminalGames::Snake::Game.new
|
31
|
+
end
|
32
|
+
|
33
|
+
if game
|
34
|
+
method = case command.downcase
|
35
|
+
when /play/
|
36
|
+
game.play!
|
37
|
+
when /about/
|
38
|
+
puts game.about
|
39
|
+
else
|
40
|
+
puts help
|
41
|
+
end
|
42
|
+
else
|
43
|
+
puts help
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'curses'
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
module RubyTerminalGames
|
6
|
+
UP = 0
|
7
|
+
RIGHT = 1
|
8
|
+
DOWN = 2
|
9
|
+
LEFT = 3
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative 'ruby_terminal_games/game'
|
13
|
+
require_relative 'ruby_terminal_games/board'
|
14
|
+
require_relative 'ruby_terminal_games/keyboard'
|
15
|
+
require_relative 'ruby_terminal_games/snake'
|
16
|
+
|
17
|
+
KEYBOARD = RubyTerminalGames::Keyboard.new
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
module RubyTerminalGames
|
3
|
+
class Board
|
4
|
+
attr_accessor :rows, :cols, :width, :height
|
5
|
+
def initialize(width: nil, height: nil)
|
6
|
+
@rows, @cols = STDOUT.winsize
|
7
|
+
@width = width || @cols
|
8
|
+
@height = height || @rows
|
9
|
+
end
|
10
|
+
|
11
|
+
def clear!
|
12
|
+
move_cursor(1, 1)
|
13
|
+
rows.times { write(" " * cols) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(text, row: nil, col: nil)
|
17
|
+
move_cursor(row, col) if (row && col)
|
18
|
+
STDOUT.write(text)
|
19
|
+
end
|
20
|
+
|
21
|
+
def move_cursor(row, col)
|
22
|
+
write("\e[#{row};#{col}H")
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw_border!
|
26
|
+
# Left and right border
|
27
|
+
(0..height).each do |i|
|
28
|
+
write("│", row: i, col: 0)
|
29
|
+
write("│", row: i, col: width)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Top and bottom border
|
33
|
+
(0..width).each do |i|
|
34
|
+
write("─", row: 0, col: i)
|
35
|
+
write("─", row: height, col: i)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Corners
|
39
|
+
write("┌", row: 0, col: 0)
|
40
|
+
write("┘", row: height, col: width)
|
41
|
+
write("┐", row: 0, col: width)
|
42
|
+
write("└", row: height, col: 0)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RubyTerminalGames
|
2
|
+
class Keyboard
|
3
|
+
attr_reader :thread
|
4
|
+
|
5
|
+
def capture(detect_direction: false, &block)
|
6
|
+
Curses.noecho
|
7
|
+
Curses.stdscr.keypad(true)
|
8
|
+
Curses.curs_set(0)
|
9
|
+
Curses.cbreak
|
10
|
+
Curses.raw
|
11
|
+
|
12
|
+
Thread.new do
|
13
|
+
loop do
|
14
|
+
key = Curses.getch
|
15
|
+
command = direction(key) || key
|
16
|
+
command = key unless detect_direction
|
17
|
+
block.call(command)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def direction(key)
|
25
|
+
case key
|
26
|
+
when Curses::KEY_UP; then UP;
|
27
|
+
when Curses::KEY_DOWN; then DOWN;
|
28
|
+
when Curses::KEY_RIGHT; then RIGHT;
|
29
|
+
when Curses::KEY_LEFT; then LEFT;
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RubyTerminalGames
|
2
|
+
module Snake
|
3
|
+
class Apple
|
4
|
+
attr_reader :position, :width, :height
|
5
|
+
def initialize(width:, height:)
|
6
|
+
@width = width
|
7
|
+
@height = height
|
8
|
+
@position = [height - 4, width - 4]
|
9
|
+
end
|
10
|
+
|
11
|
+
def move(snake)
|
12
|
+
begin
|
13
|
+
moved = [
|
14
|
+
rand(height - 4) + 2,
|
15
|
+
rand(width - 4) + 2
|
16
|
+
]
|
17
|
+
end while snake.used?(moved)
|
18
|
+
@position = moved
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module RubyTerminalGames
|
2
|
+
module Snake
|
3
|
+
class Board < RubyTerminalGames::Board
|
4
|
+
def initialize(width: nil, height: nil)
|
5
|
+
super
|
6
|
+
@width = cols
|
7
|
+
@height = rows - 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def print_world!(game)
|
11
|
+
clear!
|
12
|
+
draw_border!
|
13
|
+
draw_exit_instructions!
|
14
|
+
draw_apple!(game.apple)
|
15
|
+
draw_stats!(game.counter, game.points)
|
16
|
+
draw_snake!(game.snake.state, game.direction)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def draw_stats!(speed, points)
|
22
|
+
stats = [
|
23
|
+
"SPEED:", speed + 1, "POINTS:", points
|
24
|
+
].join(' ')
|
25
|
+
write(stats, row: rows, col: 0)
|
26
|
+
end
|
27
|
+
|
28
|
+
def draw_exit_instructions!
|
29
|
+
text = "Press Q to exit"
|
30
|
+
write(text, row: rows, col: cols - text.length)
|
31
|
+
end
|
32
|
+
|
33
|
+
def draw_apple!(apple)
|
34
|
+
row, col = apple.position
|
35
|
+
write("❤", row: row, col: col)
|
36
|
+
end
|
37
|
+
|
38
|
+
def draw_snake!(snake_state, direction)
|
39
|
+
snake_state.each do |state|
|
40
|
+
index, pos = state
|
41
|
+
head = (index == snake_state.length - 1)
|
42
|
+
draw_snake_body(direction, pos, head: head)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def snake_head(position)
|
47
|
+
case position
|
48
|
+
when UP then '∩'
|
49
|
+
when RIGHT then '⊃'
|
50
|
+
when DOWN then '∪'
|
51
|
+
when LEFT then '⊂'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def draw_snake_body(direction, position, head: false)
|
56
|
+
row, col = position
|
57
|
+
text = head ? snake_head(direction) : '◆'
|
58
|
+
write(text, row: row, col: col)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative 'board'
|
2
|
+
require_relative 'snake'
|
3
|
+
require_relative 'apple'
|
4
|
+
|
5
|
+
module RubyTerminalGames
|
6
|
+
module Snake
|
7
|
+
class Game < RubyTerminalGames::Game
|
8
|
+
attr_reader :board, :snake, :direction, :apple,
|
9
|
+
:points, :speed, :counter
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@direction = RIGHT
|
13
|
+
@board = Board.new
|
14
|
+
|
15
|
+
@apple = Apple.new(
|
16
|
+
width: @board.width,
|
17
|
+
height: @board.height
|
18
|
+
)
|
19
|
+
|
20
|
+
@snake = Snake.new(
|
21
|
+
width: @board.width,
|
22
|
+
height: @board.height
|
23
|
+
)
|
24
|
+
|
25
|
+
@points = 0
|
26
|
+
@speed = 0
|
27
|
+
@counter = 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def play!
|
31
|
+
@playing = true
|
32
|
+
|
33
|
+
KEYBOARD.capture(detect_direction: true) do |key|
|
34
|
+
exit if key =~ /q/i
|
35
|
+
next unless direction_allowed?(key)
|
36
|
+
@direction = key
|
37
|
+
end
|
38
|
+
|
39
|
+
while @playing
|
40
|
+
eat?
|
41
|
+
snake.move!(direction)
|
42
|
+
snake.died? and @playing = false
|
43
|
+
game_interval!
|
44
|
+
board.print_world!(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def about
|
49
|
+
[
|
50
|
+
"Remember the good old times. Use the keyboard ",
|
51
|
+
"arrows to control the snake."
|
52
|
+
].join
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def eat?
|
58
|
+
return unless snake.eat?(apple)
|
59
|
+
snake.grow!(direction)
|
60
|
+
add_points!
|
61
|
+
increase_speed!
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_points!
|
65
|
+
@points += (1 * @counter += 1)
|
66
|
+
end
|
67
|
+
|
68
|
+
def increase_speed!
|
69
|
+
@speed += 0.0005
|
70
|
+
end
|
71
|
+
|
72
|
+
def game_interval!
|
73
|
+
sleep([0.1 - @speed, 0].max)
|
74
|
+
end
|
75
|
+
|
76
|
+
def direction_allowed?(dir)
|
77
|
+
allowed = case @direction
|
78
|
+
when UP, DOWN
|
79
|
+
[LEFT, RIGHT]
|
80
|
+
when LEFT, RIGHT
|
81
|
+
[UP, DOWN]
|
82
|
+
end
|
83
|
+
allowed.include?(dir)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module RubyTerminalGames
|
2
|
+
module Snake
|
3
|
+
class Snake
|
4
|
+
attr_reader :state, :width, :height
|
5
|
+
def initialize(width:, height:)
|
6
|
+
@width = width
|
7
|
+
@height = height
|
8
|
+
@state = [
|
9
|
+
[0, [2, 2]],
|
10
|
+
[1, [2, 3]],
|
11
|
+
[2, [2, 4]],
|
12
|
+
[3, [2, 5]],
|
13
|
+
[4, [2, 6]],
|
14
|
+
[5, [2, 7]],
|
15
|
+
[6, [2, 8]],
|
16
|
+
[7, [2, 9]]
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def move!(direction)
|
21
|
+
*tail, head = state
|
22
|
+
(0..state.length-2).each do |i|
|
23
|
+
i, _ = tail[i]
|
24
|
+
tail[i] = [i, state[i + 1].last]
|
25
|
+
end
|
26
|
+
|
27
|
+
index, position = head
|
28
|
+
moved = update_position(position, direction)
|
29
|
+
@state = tail.concat([[index, moved]])
|
30
|
+
end
|
31
|
+
|
32
|
+
def died?
|
33
|
+
_, position = state.last
|
34
|
+
row, col = position
|
35
|
+
across_rows_limit?(row) || across_cols_limit?(col)
|
36
|
+
end
|
37
|
+
|
38
|
+
def eat?(apple)
|
39
|
+
eat = used_positions.include?(apple.position)
|
40
|
+
apple.move(self) if eat
|
41
|
+
eat
|
42
|
+
end
|
43
|
+
|
44
|
+
def grow!(direction)
|
45
|
+
last_state = @state.last
|
46
|
+
index, pos = last_state
|
47
|
+
moved = update_position(pos, direction)
|
48
|
+
@state.concat([[index + 1, moved]])
|
49
|
+
end
|
50
|
+
|
51
|
+
def used?(position)
|
52
|
+
used_positions.include?(position)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def update_position(position, direction)
|
58
|
+
row, col = position
|
59
|
+
case direction
|
60
|
+
when UP then [row - 1, col]
|
61
|
+
when RIGHT then [row, col + 1]
|
62
|
+
when DOWN then [row + 1, col]
|
63
|
+
when LEFT then [row, col - 1]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def across_rows_limit?(row)
|
68
|
+
(row < 2 || row > height - 1)
|
69
|
+
end
|
70
|
+
|
71
|
+
def across_cols_limit?(col)
|
72
|
+
(col < 2 || col > width - 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def used_positions
|
76
|
+
state.map { |s| s.last }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_terminal_games
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Alves
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-16 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: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Simple basic bash game implementations in Ruby.
|
70
|
+
email: daniel@danielalves.me
|
71
|
+
executables:
|
72
|
+
- ruby_terminal_games
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- README.md
|
77
|
+
- bin/ruby_terminal_games
|
78
|
+
- lib/ruby_terminal_games.rb
|
79
|
+
- lib/ruby_terminal_games/board.rb
|
80
|
+
- lib/ruby_terminal_games/game.rb
|
81
|
+
- lib/ruby_terminal_games/keyboard.rb
|
82
|
+
- lib/ruby_terminal_games/snake.rb
|
83
|
+
- lib/ruby_terminal_games/snake/apple.rb
|
84
|
+
- lib/ruby_terminal_games/snake/board.rb
|
85
|
+
- lib/ruby_terminal_games/snake/game.rb
|
86
|
+
- lib/ruby_terminal_games/snake/snake.rb
|
87
|
+
- lib/ruby_terminal_games/version.rb
|
88
|
+
homepage: https://github.com/alvesdan/ruby_terminal_games
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Ruby Terminal Games
|
112
|
+
test_files: []
|