Skip to content

Instantly share code, notes, and snippets.

@rabbitt
Last active April 16, 2016 02:41
Show Gist options
  • Save rabbitt/9668062 to your computer and use it in GitHub Desktop.
Save rabbitt/9668062 to your computer and use it in GitHub Desktop.
interactive rubywarrior
require 'readline'
require 'terminfo'
require 'abbrev'
require 'singleton'
$stty_save = %x{stty -g}.chomp
Signal.trap('WINCH', ->(*a) { Readline.set_screen_size(TermInfo.screen_size[0], TermInfo.screen_size[1]) })
Signal.trap('INT', ->(*a) { system('stty', $stty_save); exit! })
Signal.trap('QUIT', ->(*a) { system('stty', $stty_save); exit! })
DIRECTIONS = %i( forward backward left right ) unless defined? DIRECTIONS
class RubyWarrior::Units::Warrior
# def shoot_power() 30; end
def character
case position.direction
when :north then '↑'
when :east then '→'
when :south then '↓'
when :west then '←'
end
end
unless instance_methods.include? :perform_turn_without_safety
alias_method :perform_turn_without_safety, :perform_turn
end
def perform_turn
perform_turn_without_safety
rescue RuntimeError => e
puts e.message
@current_turn.instance_variable_set(:@action, nil)
play_turn(@current_turn)
retry
end
unless instance_methods.include? :play_turn_without_safety
alias_method :play_turn_without_safety, :play_turn
end
def play_turn(turn)
play_turn_without_safety(turn)
rescue RuntimeError => e
puts e.message
@current_turn.instance_variable_set(:@action, nil)
play_turn(@current_turn)
end
end
class Player
class Readline
include Singleton
class << self
def method_missing(method, *args, &block)
return super unless instance.public_methods.include? method
instance.public_send(method, *args, &block)
end
end
attr_accessor :prompt, :choices
def initialize
@prompt = '?'
@choices = []
end
def readline
::Readline.completion_append_character = " "
::Readline.completion_proc = ->(s) { choices.grep(/^#{Regexp.escape(s)}/) }
::Readline.readline(prompt, true).tap do |response|
return '' if response.nil?
::Readline::HISTORY.pop if response !~ /\S/ || ::Readline::HISTORY.to_a[-2] == response
end
end
end
def initialize
@warrior = nil
@abilities = []
Readline.prompt = 'Enter Command (? for commands, [tab] to complete): '
end
def commands
(@abilities | DIRECTIONS | %w(exit console quit ?)).tap { |coms|
coms << :heal if @warrior.respond_to? :rest!
coms << :feel if @warrior.respond_to? :feel
coms << :look if @warrior.respond_to? :look
coms << :stairs if @warrior.respond_to? :direction_of_stairs
}
end
def command_lookup(command)
abbreviated_command_lookup = Hash[
commands.collect(&:to_s).abbrev.collect { |key,value| [key.to_sym, value.to_sym] }
][command] || command
end
def play_turn(warrior)
@abilities = warrior.methods(false).grep(/\!/).sort.reject { |m| m == :action }
Readline.choices = commands.collect(&:to_s)
# once we have the ability to feel, we can grab the warrior object
if warrior.respond_to? :feel
space = warrior.feel :forward
@warrior = space.instance_variable_get(:@floor).unique_units.find { |u|
u.is_a? RubyWarrior::Units::Warrior
}
end
begin
response = get_user_input(commands)
response.collect! { |word| command_lookup(word) }
case response.first.to_s
when 'quit', 'exit' then
exit!
when '?' then
puts "Available Actions: #{commands.collect(&:to_s).join(', ')}"
redo
when 'feel', 'look', 'feel_around' then
if warrior.respond_to? response.first
objects = examine_spaces(warrior.send(*response.collect(&:to_sym)))
direction = case response[1]
when :forward, nil then 'ahead of'
when :backward then 'behind'
end
puts "You see #{direction} you:\n #{objects.join("\n ")}"
else
puts "Unable to #{response.first} right now."
end
redo
when 'console' then
begin
require 'pry'
binding.pry
rescue LoadError
puts "Pry library not installed. Install using: gem install pry"
redo
end
redo
when 'heal' then
response = [:rest!]
break
when 'stairs' then
response = [:walk!, warrior.direction_of_stairs]
break
when /(#{DIRECTIONS.join('|')})/i then
response = [:walk!, response.first]
break
when *@abilities.collect(&:to_s) then
break
else
puts "Unknown or ambiguous command: '#{response.first}'"
next
end
end until commands.include?(response.first)
warrior.send(*response.collect(&:to_sym))
end
private
def examine_spaces(spaces)
spaces.collect { |space| describe_space(space) }
end
def describe_space(space)
description = if space.unit
case true
when space.golem? then 'A Golem'
when space.player? then 'You'
when space.enemy? then
if space.unit.name[0] =~ /[aeiou]/i
"An #{space.unit.name} (#{space.unit.health} / #{space.unit.max_health})"
else
"A #{space.unit.name} (#{space.unit.health} / #{space.unit.max_health})"
end
when space.captive? then 'A captive'
when space.ticking? then 'A bomb!'
else 'An unknown Unit'
end
else
case true
when space.wall? then 'A wall'
when space.stairs? then 'Stairs'
when space.empty? then 'Empty floor'
else 'A mysterious space'
end
end
end
def score
@warrior ? @warrior.score : 0
end
def score_text
"Score: #{score}"
end
def health_text
return "Health [ ? / ? ]" unless @warrior
"Health: [%s / %s]" % [ @warrior.health, @warrior.max_health]
end
def weapon_text
weapons = []
if @abilities.include? :attack!
power = @warrior ? @warrior.attack_power : '?'
weapons << "sword(#{power})"
end
if @abilities.include? :shoot!
power = @warrior ? @warrior.shoot_power : '?'
weapons << "bow(#{power})"
end
weapons = %w'none' if weapons.empty?
"Weapons: #{weapons.join(' / ')}"
end
def readline
Readline.readline
end
def get_user_input(commands)
puts "\n#{health_text} | #{weapon_text} | #{score_text}"
response = readline.to_s.downcase.strip.split(/\s+/).collect(&:to_sym)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment