Skip to content

Instantly share code, notes, and snippets.

@danlucraft
Created April 21, 2011 22:33
Show Gist options
  • Save danlucraft/935622 to your computer and use it in GitHub Desktop.
Save danlucraft/935622 to your computer and use it in GitHub Desktop.
Bot for playing rdrc2 Enter the Matrix game
require 'pp'
require 'rubygems'
require 'mechanize'
class MatrixInterface
CREDS = File.read("creds.txt").split(",")
def initialize
login_page = agent.get("http://reddirtrubyconf.com/login")
form = login_page.form_with(:action => "/login")
form.email = CREDS[0]
form.password = CREDS[1].chomp
form.submit
end
def agent
@agent ||= Mechanize.new
end
def fresh_agent
@agent = nil
end
def state
h1_text = game_page.parser.search("h1").text
if h1_text =~ /^You Encounter: (.*)$/
if $1.strip == "matz"
:matz
else
:encountered
end
elsif h1_text =~ /^You Are Fighting: (.*)$/
:fighting
elsif h1_text =~ /^Create a Character$/
:start
end
end
def can_execute?
assert_state(:fighting)
game_page.links_with(:href => /move=execute/).any?
end
def can_load?
assert_state(:fighting, :encountered)
!!program_loader_form
end
def execute
raise "can't execute" unless can_execute?
game_page.link_with(:href => /move=execute/).click
@game_page = nil
end
def load_programs(num_attack, num_defense)
raise "can't execute" unless can_load?
unless num_attack + num_defense == num_programs
raise "bad number of programs, need #{num_programs}"
end
program_loader_form.set_fields(
"program_attack" => num_attack.to_s,
"program_defense" => num_defense.to_s)
program_loader_form.submit
@game_page = nil
end
def player_state
assert_state(:fighting)
game_page.send(:html_body) =~ /You:\s+ (\d+)HP\s\[(.*)\]/
[$1.to_i, parse_programs($2)]
end
def computer_state
assert_state(:fighting)
game_page.send(:html_body) =~ /#{opponent_name}:\s+ (\d+)HP\s\[(.*)\]/
[$1.to_i, parse_programs($2)]
end
def opponent_name
assert_state(:fighting)
h1_text = game_page.parser.search("h1").text
h1_text =~ /^You Are Fighting: (.*)$/
$1.strip
end
def num_programs
assert_state(:fighting, :encountered)
game_page.send(:html_body) =~ /(\d+) programs/
$1.to_i
end
def max_attack
assert_state(:fighting, :encountered)
game_page.parser.search("select").
detect {|tag| tag.to_html =~ /program_attack/}.
children.map {|c| c['value'].to_i}.max
end
def max_defense
assert_state(:fighting, :encountered)
game_page.parser.search("select").
detect {|tag| tag.to_html =~ /program_defense/}.
children.map {|c| c['value'].to_i}.max
end
def create_character(name)
assert_state(:start)
f = create_character_form
f.set_fields("character[name]" => name)
f.submit
@game_page = nil
end
def game_difficulty
assert_state(:encountered)
node = game_page.parser.search("p").detect {|t| t.text =~ /This fight looks/}
node.search("b").text
end
def request_new_game
assert_state(:encountered)
game_page.link_with(:href => /move=run/).click
@game_page = nil
end
def matz!
assert_state(:matz)
link1 = game_page.link_with(:href => /move=HP/)
link2 = game_page.link_with(:href => /move=A/)
link3 = game_page.link_with(:href => /move=D/)
link4 = game_page.link_with(:href => /move=W/)
link5 = game_page.link_with(:href => /move=H/)
# [link1, link2, link3][rand(3)].click
link1.click
@game_page = nil
end
private
def assert_state(*desired_states)
raise "not #{desired_states.inspect}" unless desired_states.include?(state)
end
def game_page
@game_page ||= agent.get("http://reddirtrubyconf.com/game")
end
def program_loader_form
game_page.forms.detect do |f|
f.form_node.to_html =~ /Select a total/ && f.action == "/game/encounter"
end
end
def create_character_form
game_page.forms.detect do |f|
f.form_node.to_html =~ /Name/ && f.action == "/game"
end
end
def parse_programs(text)
text.split(" ").map {|pr| pr.strip}
end
end
class MatrixPlayer
attr_reader :interface
def initialize
@interface = MatrixInterface.new
puts "initialized, state is #{interface.state.inspect}"
end
def move
case interface.state
when :start
create_character
when :matz
puts "encountered matz! choosing extra hit point"
interface.matz!
when :encountered
interface.fresh_agent
puts "game offered is rated #{interface.game_difficulty.inspect}"
if ["easy"].include?(interface.game_difficulty)
puts "accepting game"
load_programs
else
puts "requesting new game"
interface.request_new_game
end
when :fighting
puts "player: #{interface.player_state.inspect}, computer: #{interface.computer_state.inspect}"
if should_execute?
execute
else
load_programs
end
end
end
def should_execute?
if interface.can_execute?
diff = next_hp_diff
# p({:diff => diff, :next_computer_attack => next_computer_attack})
if diff == :unknown
if next_computer_attack == :unknown or next_computer_attack <= 1
false
else
true
end
elsif diff > 1
true
else
if next_computer_attack > 1
true
else
false
end
end
else
false
end
end
def next_hp_diff
if player_programs.length == computer_programs.length
:unknown
elsif player_programs.length > computer_programs.length
count_type(player_programs[computer_programs.length], "A")
else
-1 * count_type(computer_programs[player_programs.length], "A")
end
end
def next_computer_attack
if computer_programs.any?
count_type(computer_programs[0], "A")
else
0
end
end
def count_type(program, type)
program.split(//).select {|t|t==type}.length
end
def player_programs
interface.player_state[1]
end
def computer_programs
interface.computer_state[1]
end
def execute
puts "executing"
interface.execute
end
def load_programs
np = interface.num_programs
ma = interface.max_attack
md = interface.max_defense
puts "loading programs, total is #{np}, max attack #{ma}, max defense #{md}, attack is #{[ma, np].min}, defense is #{[ma, np].min}"
interface.load_programs([ma, np].min, np - [ma, np].min)
end
def create_character
hash = File.read(".git/refs/heads/master").chomp[0..6]
name = "danlucraft-#{hash}"
puts "creating character #{name}"
interface.create_character(name)
end
end
player = MatrixPlayer.new
player.move
while player.interface.state != :start
sleep 0.5
player.move
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment