Learning Ruby with RubyWarrior

Preview:

DESCRIPTION

My presentation at Ruby Tuesday Taiwan #16

Citation preview

Learning Ruby withRubyWarrior

Mr. Big Cat2011/1/25

About Me

● 大貓

● I like coding● 大貓共和國: http://gaagaaga.blogspot.com● Twitter&Plurk: miaout17

Outline

● Introducing RubyWarrior● Playing RubyWarrior● Solving RubyWarrior with Metaprogramming

and DSL

What is RubyWarrior?

A game designed to teach the Ruby language and artificial intelligence in a fun, interactive way.

Who created RubyWarrior?

RubyWarrior is created by Ryan Bates, the

author of Railscasts.

Spoiler Warning!!

How to start?

In your shell:

$ gem install rubywarrior

$ rubywarrior

Demo!

How to play?

Demo!

Edit Your ScriptEdit Your ScriptRead README and

Edit Your Script

Run RubyWarrior

Jump to the next levelGain New Abilities

Level Passed?

No

Yes

What's Within?

Player

play_turn...

wall?enemy?captive?empty?stairs?ticking?

RubyWarrior::Space

RubyWarrior::Turn

attack!detonate!rescue!

rest!shoot!walk!

direction_ofdistance_of

health

feellistenlook

What's Within?

Player

play_turn...

wall?enemy?captive?empty?stairs?ticking?

RubyWarrior::Space

RubyWarrior::Turn

attack!detonate!rescue!

rest!shoot!walk!

direction_ofdistance_of

health

feellistenlook

What's Within?

Player

play_turn...

wall?enemy?captive?empty?stairs?ticking?

RubyWarrior::Space

RubyWarrior::Turn

attack!detonate!rescue!

rest!shoot!walk!

direction_ofdistance_of

health

feellistenlook

What's Within?

Player

play_turn...

wall?enemy?captive?empty?stairs?ticking?

RubyWarrior::Space

RubyWarrior::Turn

attack!detonate!rescue!

rest!shoot!walk!

direction_ofdistance_of

health

feellistenlook

Epic Mode

Demo!

Level 1

Level 2

Level 3

Level 4

Level 5

Level 6

Level 7

Level 8

Level 9

Level 1

Level 2

Level 3

Level 4

Level 5

Level 6

Level 7

Level 8

Level 9

Beginner Intermediate

EpicMode

EpicMode

My Epic Mode Solution

● An AI logic that pass intermediate epic mode with all-S rank

● Three implementation styles: ● Naive Solution● Pure-OO Solution● Metaprogramming&DSL Solution

Naive SolutionDIRS.each do |dir| if @warrior.feel(dir).ticking? @warrior.rescue!(dir) return endend

return if walk_to_ticking!

DIRS.each do |dir| if should_detonate?(dir) detonate!(dir) return endend

DIRS.each do |dir| if @warrior.feel(dir).enemy? @warrior.attack!(dir) return endend

DIRS.each do |dir| if @warrior.feel(dir).captive? @warrior.rescue!(dir) return endend

walk!(direction_of_stairs)

Demo!

Pure OO Solution

Demo!

Metaprogramming&DSL Solution

● Today we won't focus on how to do metaprogramming and create a DSL

● We focus on how metaprogramming & DSL make our life easier

What's Metaprogramming?

What's Metaprogramming?

"Writing a program that writes programs."

What's Metaprogramming?

"Writing a program that writes programs."哩咧供殺小?

We use attr_accessor everyday

class Warrior attr_accessor :healthend

w = Warrior.neww.health = 95puts w.health

What is attr_accessor?

class Warrior attr_accessor :healthend

w = Warrior.neww.health = 95puts w.health

class Warrior def health @health end def health=(val) @health = val endend

w = Warrior.neww.health = 59puts w.health

attr_acccessor is a METHOD!

Getter/Setter in Java

class Warrior{ public void setHealth(int val) { health = val; } public int getHealth() { return health; } private int health;}

public class AttrAccessor{ public static void main(String[] args) { Warrior s = new Warrior(); s.setHealth(95); System.out.println("Health = " + s.getHealth()); }}

Delegation

class Player

def hurt? return false unless @last_health return @warrior.health < @last_health end

def play_turn(warrior) @warrior = warrior if @warrior.feel.captive? @warrior.rescue! elsif !@warrior.feel.empty? @warrior.attack! elsif @warrior.health == 20 || hurt? @warrior.walk! else @warrior.rest! end @last_health = @warrior.health end

end

Delegation

class Player

def hurt? return false unless @last_health return @warrior.health < @last_health end

def play_turn(warrior) @warrior = warrior if @warrior.feel.captive? @warrior.rescue! elsif !@warrior.feel.empty? @warrior.attack! elsif @warrior.health == 20 || hurt? @warrior.walk! else @warrior.rest! end @last_health = @warrior.health end

end

class Player

def hurt? return false unless @last_health return health < @last_health end

def play_turn(warrior) @warrior = warrior if feel.captive? rescue! elsif !feel.empty? attack! elsif health == 20 || hurt? walk! else rest! end @last_health = health end

end

Delegation

Delegation Implementation

class Player

def health @warrior.health end

def feel @warrior.feel end

def rescue! @warrior.rescue! end

def attack! @warrior.attack! end

def walk! @warrior.walk! end

def rest! @warrior.rest! end

Delegation Implementation

class Player

def health @warrior.health end

def feel @warrior.feel end

def rescue! @warrior.rescue! end

def attack! @warrior.attack! end

def walk! @warrior.walk! end

def rest! @warrior.rest! end

require 'forwardable'

class Player

extend Forwardable

def_delegators :@warrior, :health, :feel, :rescue!, :attack!, :walk!, :rest!

Domain Specific Language(DSL) solution

DIRS.each do |dir| if @warrior.feel(dir).ticking? @warrior.rescue!(dir) return endend

return if walk_to_ticking!

DIRS.each do |dir| if should_detonate?(dir) detonate!(dir) return endend

DIRS.each do |dir| if @warrior.feel(dir).enemy? @warrior.attack!(dir) return endend

DIRS.each do |dir| if @warrior.feel(dir).captive? @warrior.rescue!(dir) return endend

walk!(direction_of_stairs)

require 'player_helper'

class Player

include PlayerHelper

def_strategries do directional(:rescue!, :ticking?) strategy { rest! if rest_when_ticking? } directional(:detonate!, :should_detonate?) directional(:attack!, :enemy?) directional(:rescue!, :captive?) strategy { walk!(direction_of_stairs) } end

Demo!

PlayerHelper

Player

DSL is Everywhere

● Rails Routing

resources :forums, :only => [:index, :show] do resources :posts end

namespace :admin do resources :forums do resources :posts, :except => [:new, :create] end end

DSL is Everywhere

● Rake Task

task :drink do puts "I drink juice"end

task :eat do puts "I eat sandwich"end

task :breakfast => [:drink, :eat] do puts "I ate breakfast"end

Conclusion: The Ruby way I know

Rails

ActiveRecord ActionMailer

ActionPack ActiveSupport

...

PlayerHelper

Player Rails App Rake Task

Rake

My RubyWarrior Script Rails Rake

Implementing high-level logic is incredible simple with easy-to-use API and DSL

Write API and create DSL with metaprogramming techniques

Lecture martial

● Source code is available on githubhttps://github.com/gaagaaga/warrior-tuesday● i-naive● i-oo● i-dsl● dsl-starter

● Slide will be on slideshare (coming soon)

Learning Resource

● For Beginners: ● TryRuby: http://tryruby.org/● RubyWarrior: https://github.com/ryanb/ruby-warrior● The Ruby Programming Language (Book)

● For Intermediate:● Eloquent Ruby (Book)● Metaprogramming Ruby (Book)

Any Question?