Infelizmente não e vou ter que voltar do zero porque vou usar outra linguagem. Nos meus primeiros dias aprendendo Ruby eu finalizei um bloco da notas e um gerador de senhas, recentemente eu fiz um jogo da cobrinha que segue regras bem únicas e ele funciona no terminal (nele eu usei as gems Curses, para dar mais estética ao terminal, e Set, pra ajustar a localização das armadilhas sem ficar por cima dos pontos). Se quiser ver, o código é esse: require 'curses' require 'set' class SnakeGame include Curses FIXED_HEIGHT = 40 FIXED_WIDTH = 120 TRAP_CLEANUP_TIME = 10 POINTS_FOOD = 20 POINTS_TRAP = -5 PENALTY_NO_FOOD = -30 X_POINTS_CLEANER = 10 X_CLEANER_BASE_CHANCE = 0.0003 TRAP_WEIGHT = 0.0003 MAX_CLEANER_CHANCE = 0.02 X_CLEANER_PERCENT = 0.35 MIN_SNAKE_SIZE = 10 ADD_SNAKE_SIZE = 2 GAME_SPEED = 40 LOGO = [ " ____ _ _ _ ____ _ ", " / ___|| |__ ___| | |/ ___| _ __ __ _| | _____ ", " \\___ \\| '_ \\ / _ \\ | |\\___ \\| '_ \\ / _` | |/ / _ \\", " ___) | | | | __/ | | ___) | | | | (_| | < __/", " |____/|_| |_|\\___|_|_||____/|_| |_|\\__,_|_|\\_\\___|", " " ] def initialize init_screen start_color if has_colors? check_terminal_resolution! cbreak noecho stdscr.keypad(true) curs_set(0) if has_colors? init_pair(1, COLOR_GREEN, COLOR_BLACK) init_pair(2, COLOR_YELLOW, COLOR_BLACK) init_pair(3, COLOR_CYAN, COLOR_BLACK) init_pair(4, COLOR_RED, COLOR_BLACK) init_pair(5, COLOR_WHITE, COLOR_BLACK) end @game_win = Window.new(FIXED_HEIGHT, FIXED_WIDTH, (lines - FIXED_HEIGHT) / 2, (cols - FIXED_WIDTH) / 2) @game_win.keypad(true) @max_y, @max_x = FIXED_HEIGHT - 1, FIXED_WIDTH - 1 stdscr.refresh end def check_terminal_resolution! if lines < FIXED_HEIGHT || cols < FIXED_WIDTH clear msg = "Terminal too small (Requirement: 120x40). Press any key to exit and resize the terminal." setpos(lines / 2, [0, (cols - msg.length) / 2].max) addstr(msg) refresh getch close_screen exit end end def reset_game @snake = 12.times.map { |i| [FIXED_HEIGHT/ 2, FIXED_WIDTH / 2 - i] } @direction = :right @growth_pending = 0 @score = 0 @traps_eaten = 0 @cleanups_since_food = 0 @cleaners_collected = 0 @total_cleanups = 0 @start_time = Time.now @last_cleanup = Time.now @cleaner = nil @traps = Set.new @death_reason = "" @last_shrink_score = 0 spawn_food end def spawn_food loop do @food = [rand(2..@max_y - 2), rand(2..@max_x - 2)] break unless @snake.include?(@food) || @traps.include?(@food) end end def shrink_snake!(amount = 1) if @snake.size - amount < MIN_SNAKE_SIZE @death_reason = "VERY SMALL SIZE (starvation)" return false end amount.times { @snake.pop if @snake.size > 1 } true end def spawn_cleaner loop do pos = [rand(2..@max_y - 2), rand(2..@max_x - 2)] unless @snake.include?(pos) || @traps.include?(pos) || pos == @food @cleaner = pos break end end end def run loop do reset_game return unless show_start_screen game_loop return unless show_game_over_screen end ensure close_screen end private def game_loop loop do render input = stdscr.getch break unless update(input) end end def render @game_win.clear @game_win.box('|', '-') now = Time.now total_time = (now - @start_time).to_i time_left = [0, (TRAP_CLEANUP_TIME - (now - @last_cleanup)).to_i].max # Barra de Status (Interface) @game_win.attron(color_pair(3)) @game_win.setpos(0, 2) status = [ "Score: #{@score}", "X: #{@cleaners_collected}", "Time: #{total_time}s", "Size: #{@snake.size} /+=10" ].join(" | ") @game_win.addstr(" #{status} ") @game_win.attroff(color_pair(3)) # Comida @game_win.attron(color_pair(2) | A_BOLD) @game_win.setpos(@food[0], @food[1]); @game_win.addch('0') @game_win.attroff(color_pair(2) | A_BOLD) # Armadilhas @game_win.attron(color_pair(4) | A_BOLD) @traps.each { |y, x| @game_win.setpos(y, x); @game_win.addch('-') if y < @max_y } @game_win.attroff(color_pair(4) | A_BOLD) # Limpador (X) if @cleaner @game_win.attron(color_pair(5) | A_BOLD) @game_win.setpos(@cleaner[0], @cleaner[1]); @game_win.addch('X') @game_win.attroff(color_pair(5) | A_BOLD) end # Cobra @game_win.attron(color_pair(1) | A_BOLD) @snake.each_with_index do |(y, x), i| next if y >= @max_y || x >= @max_x @game_win.setpos(y, x) @game_win.addch(i == 0 ? '$' : '=') end @game_win.attroff(color_pair(1) | A_BOLD) stdscr.timeout = GAME_SPEED @game_win.refresh end def update(input) # 1. Processamento de Input case input when KEY_UP, 'w', 'W' then @direction = :up unless @direction == :down when KEY_DOWN, 's', 'S' then @direction = :down unless @direction == :up when KEY_LEFT, 'a', 'A' then @direction = :left unless @direction == :right when KEY_RIGHT, 'd', 'D' then @direction = :right unless @direction == :left when 'q', 'Q' then return false end # Aqui fecha o CASE # 2. Cálculo da Nova Cabeça head_y, head_x = @snake.first case @direction when :up then head_y -= 1 when :down then head_y += 1 when :left then head_x -= 1 when :right then head_x += 1 end new_head = [head_y, head_x] # 3. Verificação de Colisões (Parede e Corpo) if head_y <= 0 || head_y >= @max_y || head_x <= 0 || head_x >= @max_x @death_reason = "WALL COLLISION" return false end if @snake[0...-1].include?(new_head) @death_reason = "BIT YOURSELF" return false end @snake.unshift(new_head) # 4. Lógica de Interação com Objetos if new_head == @food @score += POINTS_FOOD @cleanups_since_food = 0 @growth_pending += ADD_SNAKE_SIZE spawn_food # Lógica de Auto-Shrink if @score >= @last_shrink_score + 1000 shrink_amount = (@snake.size * 0.30).to_i if @snake.size > MIN_SNAKE_SIZE actual_reduction = [shrink_amount, @snake.size - MIN_SNAKE_SIZE].min actual_reduction.times { @snake.pop } end @last_shrink_score = (@score / 1000) * 1000 end # Spawna armadilhas ao comer 8.times do pos = [rand(2..@max_y - 2), rand(2..@max_x - 2)] @traps << pos unless @snake.include?(pos) || pos == @food end elsif @cleaner && new_head == @cleaner remove_count = (@traps.size * X_CLEANER_PERCENT).ceil @traps.to_a.sample(remove_count).each { |t| @traps.delete(t) } @cleaner = nil @score += X_POINTS_CLEANER @cleaners_collected += 1 elsif @traps.include?(new_head) @score += POINTS_TRAP @traps.delete(new_head) @traps_eaten += 1 return false unless shrink_snake!(1) if @traps_eaten >= 2 return false unless shrink_snake!(1) @traps_eaten = 0 end end # 5. Crescimento/Movimento da Cauda if @growth_pending > 0 @growth_pending -= 1 else @snake.pop unless new_head == @food end # 6. Penalidade por Tempo e Limpeza Automática if Time.now - @last_cleanup >= TRAP_CLEANUP_TIME @traps.to_a.first(4).each { |t| @traps.delete(t) } @last_cleanup = Time.now @total_cleanups += 1 @cleanups_since_food += 1 if @cleanups_since_food >= 2 @score += PENALTY_NO_FOOD @cleanups_since_food = 0 return false unless shrink_snake!(2) end end # 7. Spawn Dinâmico do Cleaner (X) if @cleaner.nil? dynamic_chance = X_CLEANER_BASE_CHANCE + (@traps.size * TRAP_WEIGHT) current_spawn_probability = [dynamic_chance, MAX_CLEANER_CHANCE].min spawn_cleaner if rand < current_spawn_probability end true end def show_start_screen @game_win.clear @game_win.box('|', '-') start_y = (@max_y / 2) - (LOGO.size / 2) - 3 LOGO.each_with_index do |line, index| if index < 6 @game_win.attron(color_pair(2) | A_BOLD) end x_pos = [0, (@max_x / 2) - (line.length / 2)].max @game_win.setpos(start_y + index, x_pos) @game_win.addstr(line) end @game_win.attron(A_DIM) draw_centered(8, "Press ENTER to start | Press Q to exit.") @game_win.attroff(A_DIM) @game_win.refresh @game_win.timeout = -1 loop do case @game_win.getch when 10, 13, KEY_ENTER, "\n" then return true when 'q', 'Q' then return false end end end def show_game_over_screen @game_win.clear @game_win.box('|', '-') final_time = (Time.now - @start_time).to_i @game_win.attron(color_pair(4) | A_BOLD) draw_centered(-2, "GAME OVER") @game_win.attron(color_pair(4) | A_BOLD) draw_centered(-1, "Cause of Death: #{@death_reason}") @game_win.attroff(color_pair(4) | A_BOLD) draw_centered(0, "Score: #{@score} | X Collected: #{@cleaners_collected} | Time: #{final_time}s") draw_centered(1, "R to Start Over or Q to Exit") @game_win.refresh stdscr.timeout = -1 loop do case stdscr.getch when 'r', 'R' then return true when 'q', 'Q' then return false end end end def draw_centered(y_offset, text) @game_win.setpos(@max_y / 2 + y_offset, [0, (@max_x / 2) - (text.length / 2)].max) @game_win.addstr(text) end end SnakeGame.new.run

Replies (2)

Parabéns amigo. Foque em ferremantas produtivas, jogos são uma grande perda de tempo, nossa vida é muito curta. Tanto consumir jogos quanto produzir jogos é perda de tempo, mesmo que ganhe dinheiro com isso. Faço analogia de jogos com bebidas alcólicas e drogas, se consumir está ferrado se vender vai ferrar outros. Quanto a liguagem, já que esta aprendendo rust do zero, no lugar dedique seu tempo a apreder assembly de processadores sisc como arm e risc-v que são o futuro da computação e se você dominar isso poderá fazer qualquer coisa que quiser. Linguagem de alto nível te aprisiona em bibliotecas mal otimizadas e te distancia do conhecimento pleno da máquina.