ruby_terminal_games 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|