Skip to content

k776's Beginner Code

Daniel Hug edited this page Apr 8, 2015 · 2 revisions
class Player

  DEBUG = false
  LONG_RANGE_ENEMIES = %{ a w } # the map chars for long range enemies
  MAX_HEALTH = 20
  LOW_HEALTH = 5

  def play_turn(warrior)
    @warrior = warrior
    @health ||= warrior_health
    @walk_direction ||= :forward
    @retreating ||= false

    # bunch of actions
    retreating || rest || shoot || attack || rescue_captive || turn_around || walk

    @health = warrior_health
  end

  private

  # Check if warrior can respond to various methods
  # (adds clutter, but allows this script to run 1-9 seperatly and in epic mode)
  def warrior_can?(method)
    @warrior.respond_to?(method)
  end

  # What is the warriors current health?
  # Rescue the method and return max health incase health is not available
  def warrior_health
    warrior_can?(:health) ? @warrior.health : MAX_HEALTH
  end

  # Have we lost health since last turn?
  # Compare the current health with the health at the end of last turn
  # If current health is lower, we know we've taken some damage
  def lost_health_since_last_turn?
    lost_health = (warrior_health < @health)
    puts "Player has lost health since the last turn." if lost_health && DEBUG
    lost_health
  end

  def each_direction(&block)
    [:forward, :backward, :left, :right].each do |dir|
      yield(dir)
    end
  end

  #
  # Level methods
  # Various methods for checking objects and such in our path
  #

  def objects_in_direction(direction)
    warrior_can?(:look) <br>       ? @warrior.look(direction).select { |o| !o.empty? } <br>       : Array.new
  end

  def first_object_in_direction_long_range_enemy?(direction)
    objects_in_direction(direction).first &&
      objects_in_direction(direction).first.enemy? &&
        LONG_RANGE_ENEMIES.include?(objects_in_direction(direction).first.to_map)
  end

  def within_distance_of_long_range_enemy?
    each_direction do |dir|
      return true if first_object_in_direction_long_range_enemy?(dir)
    end
    false
  end

  # is the first object in our walking path the stairs? (can we see it?)
  # not always correct (since we can only see three spaces ahead)
  def can_see_stairs?
    return false unless warrior_can?(:look)
    @warrior.look(@walk_direction).each do |space|
      if !space.empty? && !space.captive?
        puts "Player can't see stairs, enemy in direction: #{@walk_direction}" if DEBUG
        return false
      end
      if space.stairs?
        puts "Player can see stairs from current location in direction: #{@walk_direction}" if DEBUG
        return true
      end
    end
    false
  end

  def only_wall_in_walk_direction?
    only_walls = objects_in_direction(@walk_direction).first &&
                   objects_in_direction(@walk_direction).first.wall?
    puts "Only walls in direction: #{@walk_direction}" if only_walls && DEBUG
    only_walls
  end

  # are we ok to heal? to find out, we have to have less than perfect health,
  # have not been attacked since last turn, not within distance of a long
  # range fighter, and not heading directly into stairs (the last one helps
  # boost points scored in the level)
  def is_ok_to_rest?
    warrior_health < MAX_HEALTH &&
      !lost_health_since_last_turn? &&
        !within_distance_of_long_range_enemy? &&
          !can_see_stairs?
  end

  #
  # Actions
  # Return true if the player has moved (bang methods)
  # Return false if the player has not moved
  #

  def retreating
    if @retreating && !lost_health_since_last_turn?
      puts "Have not been attacked so can stop retreating." if DEBUG
      @retreating = false
      return turn_around(true) if only_wall_in_walk_direction?
    elsif lost_health_since_last_turn? && warrior_health <= LOW_HEALTH
      puts "Health is low and still being attacked. Turning around and retreating." if DEBUG
      result = turn_around(true)
      @retreating = true
      return result # turn_around returns a boolean
    end
    false
  end

  def rest
    return false unless warrior_can?(:rest!)
    if is_ok_to_rest?
      puts "Haven't been hurt since last turn, and not within reach of attacker, so healing." if DEBUG
      @warrior.rest!
      return true
    end
    false
  end

  def shoot
    return false unless warrior_can?(:shoot!)
    each_direction do |dir|
      if first_object_in_direction_long_range_enemy?(dir)
        puts "Shooting enemy in direction: #{dir.to_s}" if DEBUG
        @warrior.shoot!(dir)
        return true
      end
    end
    false
  end

  def attack
    return false unless warrior_can?(:attack!)
    each_direction do |dir|
      if @warrior.feel(dir).enemy?
        puts "Attacking enemy in direction: #{dir.to_s}" if DEBUG
        @warrior.attack!(dir)
        return true
      end
    end
    false
  end

  def rescue_captive
    return false unless warrior_can?(:rescue!)
    each_direction do |dir|
      if @warrior.feel(dir).captive?
        puts "Rescuing captive in direction: #{dir.to_s}" if DEBUG
        @warrior.rescue!(dir)
        return true
      end
    end
    false
  end

  def turn_around(quick_turn=false)
    return false unless warrior_can?(:feel)

    if @retreating
      puts "Player already retreating so not turning around." if DEBUG
      return false
    end

    reverse_dirs = {
      :forward => :backward,
      :backward => :forward,
      :left => :right,
      :right => :left
    }

    direction = reverse_dirs[@walk_direction]

    if quick_turn # won't pivot
      puts "Walking direction to direction: #{direction.to_s}" if DEBUG
      @walk_direction = direction
    elsif @warrior.feel(@walk_direction).wall?
      puts "Pivoting direction to direction: #{direction.to_s}" if DEBUG
      @walk_direction = direction
      @warrior.pivot!(direction) if warrior_can?(:pivot!)
      return true
    end

    false
  end

  def walk
    if !warrior_can?(:feel) || @warrior.feel(@walk_direction).empty?
      puts "Walking to empty space in direction: #{@walk_direction.to_s}" if DEBUG
      @warrior.walk!(@walk_direction)
    end
  end

end