Created
April 21, 2011 22:33
-
-
Save danlucraft/935622 to your computer and use it in GitHub Desktop.
Bot for playing rdrc2 Enter the Matrix game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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