Skip to content

Commit

Permalink
feat: snake movement (#3)
Browse files Browse the repository at this point in the history
* feat: snake movement

* linter
  • Loading branch information
mapra99 authored Apr 19, 2023
1 parent c2c5dee commit 84281fc
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 31 deletions.
4 changes: 0 additions & 4 deletions .byebug_history

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
.rspec_status
Gemfile.lock
.ruby-version
.byebug_history
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ Layout/LineLength:

Style/Documentation:
Enabled: false

Metrics/MethodLength:
Max: 15
13 changes: 13 additions & 0 deletions lib/rb_snake/actions/change_direction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module RbSnake
module Actions
class ChangeDirection
class << self
def call(state, new_direction:)
state.update_direction(new_direction)
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/rb_snake/actions/move_snake.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module RbSnake
module Actions
class MoveSnake
class << self
def call(state)
snake = state.snake
current_direction = state.current_direction
food = state.food
grid = state.grid

next_position = snake.next_position(current_direction)
if next_position.eql?(food.location)
snake.eat(food)
food.regenerate(snake, grid)
else
snake.move_to(next_position)
end
end
end
end
end
end
54 changes: 29 additions & 25 deletions lib/rb_snake/app.rb
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
# frozen_string_literal: true

require_relative "view/ruby2d"
require_relative "model/state"
require_relative "actions/actions"
require "rb_snake/models/state"
require "rb_snake/views/window"

# require_relative "view/ruby2d"
# require_relative "model/state"
# require_relative "actions/actions"

module RbSnake
class App
attr_reader :state, :window

def initialize
@state = Models::State.initial_state
end

def start
@view = View::Ruby2dView.new(self)
timer_thread = Thread.new { init_timer(@view) }
@view.start(@state)
timer_thread.join
end
@window = Views::Window.new(self)

def init_timer(view)
loop do
if @state.game_finished
puts "Juego Finalizado"
puts "Puntaje: #{@state.snake.positions.length}"
break
end
@state = Actions.move_snake(@state)
view.render(@state)
sleep 0.1
end
window.start(state)
end

def send_action(action, params)
new_state = Actions.send(action, @state, params)
return unless new_state.hash != @state
# def send_action(action, params)
# new_state = Actions.send(action, @state, params)
# return unless new_state.hash != @state

@state = new_state
@view.render(@state)
end
# @state = new_state
# @view.render(@state)
# end

# def init_timer(view)
# loop do
# if @state.game_finished
# puts "Juego Finalizado"
# puts "Puntaje: #{@state.snake.positions.length}"
# break
# end
# @state = Actions.move_snake(@state)
# view.render(@state)
# sleep 0.1
# end
# end
end
end
4 changes: 4 additions & 0 deletions lib/rb_snake/models/coordinate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def initialize(row:, col:)
@row = row
@col = col
end

def eql?(other)
row == other.row && col == other.col
end
end
end
end
21 changes: 21 additions & 0 deletions lib/rb_snake/models/food.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ class Food
def initialize(row:, col:)
@location = Coordinate.new(row: row, col: col)
end

def render(window)
window.remove(rendered_element) if rendered_element
@rendered_element = yield(location.row, location.col)
end

def regenerate(snake, grid)
loop do
new_row = rand(0...grid.rows)
new_col = rand(0...grid.cols)

if snake.body.none? { |position| position.row == new_row && position.col == new_col }
@location = Coordinate.new(row: new_row, col: new_col)
break
end
end
end

private

attr_reader :rendered_element
end
end
end
48 changes: 48 additions & 0 deletions lib/rb_snake/models/snake.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# frozen_string_literal: true

require "rb_snake/models/direction"
require "rb_snake/models/coordinate"

module RbSnake
module Models
class Snake
Expand All @@ -8,6 +11,51 @@ class Snake
def initialize(body:)
@body = body
end

def render(window)
rendered_body.each { |elem| window.remove(elem) } if rendered_body&.length&.positive?

@rendered_body = body.map do |position|
yield(position.row, position.col)
end
end

def next_position(direction)
new_row = head.row
new_col = head.col

case direction
when Direction::UP
new_row -= 1
when Direction::RIGHT
new_col += 1
when Direction::DOWN
new_row += 1
when Direction::LEFT
new_col -= 1
else
raise StandardError, "direction #{direction} not recognized"
end

Coordinate.new(row: new_row, col: new_col)
end

def eat(food)
body.unshift(food.location)
end

def move_to(position)
body.unshift(position)
body.pop
end

private

def head
body.first
end

attr_reader :rendered_body
end
end
end
12 changes: 10 additions & 2 deletions lib/rb_snake/models/state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ def self.initial_state
snake: Snake.new(
body: [Coordinate.new(row: 1, col: 1), Coordinate.new(row: 0, col: 1)]
),
food: Food.new(row: 4, col: 4),
grid: Grid.new(rows: 8, cols: 12),
food: Food.new(row: 10, col: 1),
grid: Grid.new(rows: 12, cols: 24),
current_direction: Direction::DOWN,
game_finished: false
)
end

def game_score
snake.positions.length
end

def update_direction(new_direction)
@current_direction = new_direction
end
end
end
end
100 changes: 100 additions & 0 deletions lib/rb_snake/views/window.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

require "ruby2d"
require "rb_snake/models/direction"
require "rb_snake/actions/move_snake"
require "rb_snake/actions/change_direction"

module Views
class Window
attr_reader :app

PIXEL_SIZE = 50

def initialize(app)
@app = app
@ruby2d_window = Ruby2D::Window.new
end

def start(state)
setup_window(state.grid)
add_key_listener(state)
render_on_each_frame(state)

ruby2d_window.show
end

private

attr_reader :ruby2d_window

def setup_window(grid)
ruby2d_window.set(
title: "Snake",
width: PIXEL_SIZE * grid.cols,
height: PIXEL_SIZE * grid.rows
)
end

def add_key_listener(state)
ruby2d_window.on :key_down do |event|
handle_key_event(event, state)
end
end

def render_on_each_frame(state)
tick = 0

ruby2d_window.update do
if (tick % 5).zero?
render(state)
::RbSnake::Actions::MoveSnake.call(state)
end

if state.game_finished
ruby2d_window.close

puts "Game Over"
puts "Score: #{state.game_score}"
end

tick += 1
end
end

def render(state)
state.food.render(ruby2d_window) do |row, col|
render_square(row: row, col: col, color: "yellow")
end

state.snake.render(ruby2d_window) do |row, col|
render_square(row: row, col: col, color: "green")
end
end

def render_square(row:, col:, color:)
square = Ruby2D::Square.new(
x: col * PIXEL_SIZE,
y: row * PIXEL_SIZE,
size: PIXEL_SIZE,
color: color
)

ruby2d_window.add(square)
square
end

def handle_key_event(event, state)
case event.key
when "up"
RbSnake::Actions::ChangeDirection.call(state, new_direction: ::RbSnake::Models::Direction::UP)
when "down"
RbSnake::Actions::ChangeDirection.call(state, new_direction: ::RbSnake::Models::Direction::DOWN)
when "left"
RbSnake::Actions::ChangeDirection.call(state, new_direction: ::RbSnake::Models::Direction::LEFT)
when "right"
RbSnake::Actions::ChangeDirection.call(state, new_direction: ::RbSnake::Models::Direction::RIGHT)
end
end
end
end
1 change: 1 addition & 0 deletions rb-snake.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
# Uncomment to register a new dependency of your gem
spec.add_dependency "ruby2d", "~> 0.12.0"

spec.add_development_dependency "byebug"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "rubocop", "~> 1.21"
Expand Down

0 comments on commit 84281fc

Please sign in to comment.