[#246252] How to use standard library? — Jamal Soueidan <jkhaledsoueidan@...>

Hello,

18 messages 2007/04/01
[#246253] Re: How to use standard library? — Stefano Crocco <stefano.crocco@...> 2007/04/01

Alle domenica 1 aprile 2007, Jamal Soueidan ha scritto:

[#246263] Re: How to use standard library? — "Yamal Soueidan" <jkhaledsoueidan@...> 2007/04/01

Well, where does it identify its module and not a class?

[#246267] Re: How to use standard library? — Stefano Crocco <stefano.crocco@...> 2007/04/01

Alle domenica 1 aprile 2007, Yamal Soueidan ha scritto:

[#246368] Map Or Collect Redux — "RubyTalk@..." <rubytalk@...>

Looking in the old archives of ruby-talk I found a thread in 2005

11 messages 2007/04/02

[#246378] Test::Unit Reports — aidy.lewis@...

Hi,

23 messages 2007/04/02

[#246464] Last iteration condition — "Mike" <michaelst@...>

Hi,

14 messages 2007/04/03

[#246590] Everything is a object? — Jamal Soueidan <jkhaledsoueidan@...>

Hello,

40 messages 2007/04/03
[#246598] Re: Everything is a object? — Jamal Soueidan <jkhaledsoueidan@...> 2007/04/04

Jamal Soueidan wrote:

[#246600] Re: Everything is a object? — Gary Wright <gwtmp01@...> 2007/04/04

[#246601] Re: Everything is a object? — Jamal Soueidan <jkhaledsoueidan@...> 2007/04/04

Gary Wright wrote:

[#246614] fast XML parser, other than libxml — Peter Szinek <peter@...>

Hello all,

20 messages 2007/04/04
[#246615] Re: fast XML parser, other than libxml — "Keith Fahlgren" <keith@...> 2007/04/04

On 4/3/07, Peter Szinek <peter@rubyrailways.com> wrote:

[#246626] Re: fast XML parser, other than libxml — Peter Szinek <peter@...> 2007/04/04

Keith Fahlgren wrote:

[#246629] Re: fast XML parser, other than libxml — Robert Klemme <shortcutter@...> 2007/04/04

On 04.04.2007 10:53, Peter Szinek wrote:

[#246630] Re: fast XML parser, other than libxml — Peter Szinek <peter@...> 2007/04/04

Robert Klemme wrote:

[#246669] Problem Extracting Array Values — Dustin Anderson <rubyforum@...>

Hi All,

16 messages 2007/04/04
[#246672] Re: Problem Extracting Array Values — "ChrisH" <chris.hulan@...> 2007/04/04

On Apr 4, 10:02 am, Dustin Anderson <rubyfo...@dustinanderson.com>

[#246673] Re: Problem Extracting Array Values — "Ryan Leavengood" <leavengood@...> 2007/04/04

On 4/4/07, ChrisH <chris.hulan@gmail.com> wrote:

[#246679] Re: Problem Extracting Array Values — Dustin Anderson <rubyforum@...> 2007/04/04

[#246702] nil? and non-existent objects — "François Montel" <zerohalo@...>

Why is it that the nil? method can sometimes be called on an object that

12 messages 2007/04/04

[#246830] Redefining initialize while staying -w clean — "Daniel Berger" <djberg96@...>

Hi all,

11 messages 2007/04/05

[#246929] Getting to 100 (#119) — Ruby Quiz <james@...>

The three rules of Ruby Quiz:

57 messages 2007/04/06
[#247191] Re: Getting to 100 (#119) — "Carl Porth" <badcarl@...> 2007/04/08

here is my first pass:

[#247192] Re: Getting to 100 (#119) — "Carl Porth" <badcarl@...> 2007/04/08

After going back and reading the current solutions, I like Ken Bloom's

[#247215] Re: Getting to 100 (#119) — "Marcel Ward" <wardies@...> 2007/04/09

On 08/04/07, Carl Porth <badcarl@gmail.com> wrote:

[#246946] A few beginners questions — wannaberor <amldcc@...>

Guys,

15 messages 2007/04/06

[#247059] Question to all you newbies (others welcome) — SonOfLilit <sonoflilit@...>

Hello everyone,

40 messages 2007/04/07
[#247078] Re: Question to all you newbies (others welcome) — Michael Brooks <michael.brooks@...> 2007/04/07

SonOfLilit wrote:

[#247097] Re: Question to all you newbies (others welcome) — "ChrisKaelin" <ck.stonedragon@...> 2007/04/07

I totally agree, what people say about a single-entry-point: ruby-

[#247099] Re: Question to all you newbies (others welcome) — James Britt <james.britt@...> 2007/04/07

ChrisKaelin wrote:

[#247100] Re: Question to all you newbies (others welcome) — "Jeff" <cohen.jeff@...> 2007/04/07

On Apr 7, 4:30 pm, James Britt <james.br...@gmail.com> wrote:

[#247131] Minimum ruby installation. — "bino_oetomo" <bino@...> 2007/04/08

Dear Experts.

[#247151] Re: Minimum ruby installation. — Alex Young <alex@...> 2007/04/08

bino_oetomo wrote:

[#247062] rb_yield(), break, and C extensions — "Noah Easterly" <noah.easterly@...>

So, I'm working on a C extension.

11 messages 2007/04/07

[#247088] Trying to GET google with socket....problem — Hey You <r3madi@...>

Well I don't know why the socket can't connect to Google. Here is my

17 messages 2007/04/07

[#247155] code blocks and methods — andy <eps@...>

Hi,

13 messages 2007/04/08

[#247299] Infinate Loop - Please Advise — "Merrie" <merries@...>

This program produces an infinate loop. I am learning from Learn to Program and do not have a clear example how to do this particular example. It is suppose to count down the bottles and repeat the phrase until it reach 0 bottles of beer then end :)

13 messages 2007/04/09

[#247338] How to Write a Spelling Corrector — Brian Adkins <lojicdotcomNOSPAM@...>

Peter Norvig wrote a simple spelling corrector in 20 lines of Python 2.5,

12 messages 2007/04/10

[#247391] Slow ruby regexes — Emmanuel <emmanuel@...>

Hello i've been reading this article, wich has a few benchmarks

47 messages 2007/04/10
[#247402] Re: Slow ruby regexes — SonOfLilit <sonoflilit@...> 2007/04/10

Read wikipedia on Regex. It explains better than I can why one is used

[#247403] Re: Slow ruby regexes — MenTaLguY <mental@...> 2007/04/10

On Wed, 11 Apr 2007 02:59:29 +0900, SonOfLilit <sonoflilit@gmail.com> wrote:

[#247409] Re: Slow ruby regexes — "Robert Dober" <robert.dober@...> 2007/04/10

On 4/10/07, MenTaLguY <mental@rydia.net> wrote:

[#247410] Re: Slow ruby regexes — MenTaLguY <mental@...> 2007/04/10

On Wed, 11 Apr 2007 03:56:28 +0900, "Robert Dober" <robert.dober@gmail.com> wrote:

[#247455] Re: Slow ruby regexes — "Robert Dober" <robert.dober@...> 2007/04/11

On 4/10/07, MenTaLguY <mental@rydia.net> wrote:

[#247456] Re: Slow ruby regexes — "Robert Dober" <robert.dober@...> 2007/04/11

oops wrong button here :(

[#247499] Re: Slow ruby regexes — MenTaLguY <mental@...> 2007/04/11

On Wed, 11 Apr 2007 16:53:26 +0900, "Robert Dober" <robert.dober@gmail.com> wrote:

[#247518] Re: Slow ruby regexes — "Robert Dober" <robert.dober@...> 2007/04/11

On 4/11/07, MenTaLguY <mental@rydia.net> wrote:

[#247541] Re: Slow ruby regexes — MenTaLguY <mental@...> 2007/04/11

On Thu, 12 Apr 2007 03:27:04 +0900, "Robert Dober" <robert.dober@gmail.com> wrote:

[#247608] Re: Slow ruby regexes — Robert Klemme <shortcutter@...> 2007/04/12

On 11.04.2007 22:51, MenTaLguY wrote:

[#247683] Re: Slow ruby regexes — MenTaLguY <mental@...> 2007/04/12

On Thu, 12 Apr 2007 16:10:06 +0900, Robert Klemme <shortcutter@googlemail.com> wrote:

[#247770] Re: Slow ruby regexes — Robert Klemme <shortcutter@...> 2007/04/13

On 12.04.2007 18:31, MenTaLguY wrote:

[#247398] ClothRed (HTML to Textile) — Phillip Gawlowski <cmdjackryan@...>

I'm pleased to announce, that I've begun working on a small library to

16 messages 2007/04/10
[#247526] Re: [ANN] ClothRed (HTML to Textile) — "Victor \"Zverok\" Shepelev" <vshepelev@...> 2007/04/11

From: Phillip Gawlowski [mailto:cmdjackryan@googlemail.com]

[#247436] NameError: uninitialized constant Date::ABBR_MONTHS — Jigar Gosar <jigar.gosar@...>

DATE::ABBR_MONTHS exists in this doc here.

13 messages 2007/04/11

[#247471] How come this doesn't work? — Hey You <r3madi@...>

require 'socket'

13 messages 2007/04/11

[#247622] What is your favourite IDE? — "ChrisKaelin" <ck.stonedragon@...>

I prefer using eclipse for it's freedom, ruby and svn plugins etc. But

95 messages 2007/04/12
[#247681] Re: What is your favourite IDE? — Todd Werth <twerth@...> 2007/04/12

ChrisKaelin wrote:

[#247980] Re: IDEs, syntactic vs. semantic highlighting, etc. — Tim X <timx@...> 2007/04/15

Chad Perrin <perrin@apotheon.com> writes:

[#247737] Re: What is your favourite IDE? — Vlad Ciubotariu <vcciubot@...> 2007/04/12

Is anyone using Activestate's Kodomo? I know activestate is a player in

[#247757] Re: What is your favourite IDE? — "M. Edward (Ed) Borasky" <znmeb@...> 2007/04/13

Vlad Ciubotariu wrote:

[#247913] Re: What is your favourite IDE? Eclipse DLTK! — Tim X <timx@...> 2007/04/14

Todd Werth <twerth@infinitered.com> writes:

[#247636] Re: What is your favourite IDE? — "Alexey Kalmykov" <akalmykov@...>

15 messages 2007/04/12

[#247725] SNMP agent library? — "Marcus Bristav" <marcus.bristav@...>

I need to write an SNMP agent (raise traps and expose MIBs). Is there

15 messages 2007/04/12
[#247741] Re: SNMP agent library? — "Francis Cianfrocca" <garbagecat10@...> 2007/04/13

On 4/12/07, Marcus Bristav <marcus.bristav@gmail.com> wrote:

[#247790] Re: SNMP agent library? — "Marcus Bristav" <marcus.bristav@...> 2007/04/13

Hi Francis,

[#247809] Re: SNMP agent library? — "Francis Cianfrocca" <garbagecat10@...> 2007/04/13

On 4/13/07, Marcus Bristav <marcus.bristav@gmail.com> wrote:

[#247760] Idiom wanted (now hiring!) — Jonathan <terhorst@...>

Is there a cool way to do this without calling the function twice?:

28 messages 2007/04/13
[#247767] Re: Idiom wanted (now hiring!) — Joel VanderWerf <vjoel@...> 2007/04/13

Jonathan wrote:

[#247783] Re: Idiom wanted (now hiring!) — "Robert Dober" <robert.dober@...> 2007/04/13

On 4/13/07, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

[#247805] Magic Fingers (#120) — Ruby Quiz <james@...>

The three rules of Ruby Quiz:

14 messages 2007/04/13

[#247974] executing a system command and stopping it after a specified duration? — Robert La Ferla <robertlaferla@...>

I'd like to run a system command and then stop it after specified

9 messages 2007/04/15

[#248026] translate Perl diamond operator to Ruby — Chad Perrin <perrin@...>

Over the years, I've found the following to be an excellent way to whip

13 messages 2007/04/15

[#248151] factorial in ruby — "Trans" <transfire@...>

Is factorial defined anywhere in Ruby's core or standard library. If

21 messages 2007/04/16
[#248154] Re: factorial in ruby — "Jason Roelofs" <jameskilton@...> 2007/04/16

No and most likely not.

[#248245] Timeout errors using Net::HTTP on Windows — Toby DiPasquale <toby@...>

Hi all,

12 messages 2007/04/17

[#248255] new — "poison tooth" <fixxie.wits@...>

Im just learning ruby and im stuck the guide im using says

17 messages 2007/04/17

[#248263] how to have a default argument — "shawn bright" <nephish@...>

hello all,

11 messages 2007/04/17

[#248384] ruby scripting on microsoft active directory plus exchange — Pe, Botp <botp@...>

Hi All,

16 messages 2007/04/19
[#248445] Re: ruby scripting on microsoft active directory plus exchange — "Glen Holcomb" <damnbigman@...> 2007/04/19

I would recommend looking at Net::LDAP: gem install ruby-net-ldap

[#248463] Re: ruby scripting on microsoft active directory plus exchange — "Ball, Donald A Jr (Library)" <donald.ball@...> 2007/04/19

> I would recommend looking at Net::LDAP: gem install ruby-net-ldap

[#248516] what does this code do ? from libxml schema-test.rb ??? — "aktxyz@..." <aktxyz@...>

At the bottom of the schema-test.rb in the libxml gem, there is this

13 messages 2007/04/20
[#248522] Re: what does this code do ? from libxml schema-test.rb ??? — Reuben Grinberg <reuben.grinberg@...> 2007/04/20

aktxyz@gmail.com wrote:

[#248546] Morse Code (#121) — Ruby Quiz <james@...>

The three rules of Ruby Quiz:

32 messages 2007/04/20

[#248629] Tracking down a garbage collection problem — Wincent Colaiuta <win@...>

I'm trying to work out ways to reduce the memory use of one of my

12 messages 2007/04/21

[#248680] GameR 0.2 is out — Wim Vander Schelden <wim.vanderschelden@...>

I've released GameR, a small and simple game development framework for Ruby.

13 messages 2007/04/22

[#248744] Arrow operator with dash instead of equals (->) — Andrew Green <ndrw_grn@...>

Hi, all,

16 messages 2007/04/22
[#248747] Re: Arrow operator with dash instead of equals (->) — Timothy Hunter <TimHunter@...> 2007/04/22

Andrew Green wrote:

[#248750] Re: Arrow operator with dash instead of equals (->) — Andrew Green <ndrw_grn@...> 2007/04/23

> > Is it possible to use -> as a method name in Ruby?

[#248762] Question regarding design of the String Class — "Michael W. Ryder" <_mwryder@...>

Was there a reason the string class was implemented with str[i]

21 messages 2007/04/23
[#248774] Re: Question regarding design of the String Class — Daniel Martin <martin@...> 2007/04/23

"Michael W. Ryder" <_mwryder@worldnet.att.net> writes:

[#248777] Ruby Reports 1.0 RC1 (0.10.0) — "Gregory Brown" <gregory.t.brown@...>

== Ruby Reports 1.0, Release Candidate 1 (0.10.0) ==

13 messages 2007/04/23

[#248814] unix zcat with ruby? — music <music@...>

I have to read in many files.

14 messages 2007/04/23

[#248862] ruby and C — "smc smc" <fixxie.wits@...>

Would it be easier to learn ruby if i knew C/C+/C++ or the other way around?

14 messages 2007/04/24

[#248981] file-find 0.1.0 — Daniel Berger <djberg96@...>

Hi all,

18 messages 2007/04/24
[#248984] Re: [ANN] file-find 0.1.0 — "Leslie Viljoen" <leslieviljoen@...> 2007/04/24

On 4/24/07, Daniel Berger <djberg96@gmail.com> wrote:

[#248993] Re: [ANN] file-find 0.1.0 — "Daniel Berger" <djberg96@...> 2007/04/24

On 4/24/07, Leslie Viljoen <leslieviljoen@gmail.com> wrote:

[#249027] Using Watir and Ruby2Exe together — Jim Clark <diegoslice@...>

I've been asked to help solve a browser issue that I think Watir and

13 messages 2007/04/25

[#249034] C++ code into Ruby, I need it fast, no time for RTFM — Andrei Ursan <steelheart222@...>

[code]

41 messages 2007/04/25
[#249041] Re: C++ code into Ruby, I need it fast, no time for RTFM — John Joyce <dangerwillrobinsondanger@...> 2007/04/25

[#249043] Re: C++ code into Ruby, I need it fast, no time for RTFM — Andrei Ursan <steelheart222@...> 2007/04/25

> Translate this for me, right now. No, by yesterday. == A time when

[#249044] Re: C++ code into Ruby, I need it fast, no time for RTFM — "David Jones" <tafftoo@...> 2007/04/25

There are still ways to ask for things.

[#249060] Is it possible to make system use bash instead of sh? — Wai Tsang <simotsa@...>

Hi,

12 messages 2007/04/25

[#249076] DHH vs. WHY style — Trans <transfire@...>

Like to know others general opinions on having a comprehensive library

35 messages 2007/04/25

[#249226] 10 millisecond delay/callback — Earle Clubb <eclubb@...>

I need to perform a task every 10ms. I've been using

21 messages 2007/04/26
[#249228] Re: 10 millisecond delay/callback — khaines@... 2007/04/26

On Fri, 27 Apr 2007, Earle Clubb wrote:

[#249238] Using Ruby in a Corporate Environment — Steve Molitor <stevemolitor@...>

---------- Forwarded message ----------

10 messages 2007/04/26

[#249268] Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — "Gregory Brown" <gregory.t.brown@...>

Hi Folks,

24 messages 2007/04/27
[#249334] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — "Lyle Johnson" <lyle.johnson@...> 2007/04/27

On 4/27/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:

[#249338] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — "Jamey Cribbs" <jcribbs@...> 2007/04/27

Lyle Johnson wrote:

[#249340] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — "Gregory Brown" <gregory.t.brown@...> 2007/04/27

On 4/27/07, Jamey Cribbs <jcribbs@netpromi.com> wrote:

[#249342] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — John Joyce <dangerwillrobinsondanger@...> 2007/04/27

[#249343] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — "Gregory Brown" <gregory.t.brown@...> 2007/04/27

On 4/27/07, John Joyce <dangerwillrobinsondanger@gmail.com> wrote:

[#249347] Re: Looking for thoughts and opinions on Ruport, and reporting in Ruby in general. — John Joyce <dangerwillrobinsondanger@...> 2007/04/27

[#249269] Output A File w/ Line Numbers? — John Joyce <dangerwillrobinsondanger@...>

I'd like to read a file and output its contents (just to terminal is

18 messages 2007/04/27
[#249414] Re: Output A File w/ Line Numbers? — "Robert Dober" <robert.dober@...> 2007/04/28

On 4/27/07, John Joyce <dangerwillrobinsondanger@gmail.com> wrote:

[#249274] string replacement... — Josselin <josselin@...>

I have a string : str = "/proposal/list/31551"

15 messages 2007/04/27

[#249315] Checking Credit Cards (#122) — Ruby Quiz <james@...>

The three rules of Ruby Quiz:

65 messages 2007/04/27

[#249430] cyclic array — Josselin <josselin@...>

I would like to print n elements from an Array in a cyclic way.

18 messages 2007/04/28

[#249524] Array.which_long? ( I coded an extension for Array ) — "Billy Hsu" <ruby.maillist@...>

Hi, I'm CFC

31 messages 2007/04/29
[#249527] Re: Array.which_long? ( I coded an extension for Array ) — "David A. Black" <dblack@...> 2007/04/29

Hi --

[#249531] Re: Array.which_long? ( I coded an extension for Array ) — "Robert Dober" <robert.dober@...> 2007/04/29

On 4/29/07, David A. Black <dblack@wobblini.net> wrote:

[#249532] Re: Array.which_long? ( I coded an extension for Array ) — "David A. Black" <dblack@...> 2007/04/29

Hi --

[#249526] Re: Array.which_long? ( I coded an extension for Array ) — "Chris Carter" <cdcarter@...> 2007/04/29

On 4/29/07, Billy Hsu <ruby.maillist@gmail.com> wrote:

[#249664] Re: Array.which_long? ( I coded an extension for Array ) — Robert Klemme <shortcutter@...> 2007/04/30

On 29.04.2007 16:11, Chris Carter wrote:

[#249667] Re: Array.which_long? ( I coded an extension for Array ) — "Robert Dober" <robert.dober@...> 2007/04/30

On 4/30/07, Robert Klemme <shortcutter@googlemail.com> wrote:

[#249670] Re: Array.which_long? ( I coded an extension for Array ) — Robert Klemme <shortcutter@...> 2007/04/30

On 30.04.2007 12:39, Robert Dober wrote:

[#249688] Re: Array.which_long? ( I coded an extension for Array ) — "Robert Dober" <robert.dober@...> 2007/04/30

On 4/30/07, Robert Klemme <shortcutter@googlemail.com> wrote:

[#249587] Class Level Variables — Cory <coryw@...>

Alright, I'm missing some core ruby concept here that I just can't

23 messages 2007/04/30
[#249589] Re: Class Level Variables — Ari Brown <ari@...> 2007/04/30

[#249603] sorting by rand? — seebs@... (Peter Seebach)

Browsing something at a bookstore recently, I saw an example of

22 messages 2007/04/30

[#249689] RoR how does scaffold work? — anansi <kazaam@...>

Hi,

17 messages 2007/04/30

[#249691] ruby and true — aidy.lewis@...

Hi,

16 messages 2007/04/30

[#249759] relocatable ruby distribution — "fkc_email-news @ yahoo dot com" <fkchang2000@...>

Hi All:

11 messages 2007/04/30

Re: [QUIZ] Magic Fingers (#120)

From: Jesse Merriman <jesse.d.merriman@...>
Date: 2007-04-19 02:24:25 UTC
List: ruby-talk #248385
My original solution to this quiz had a lot of problems, but I think I fixed
them now. I re-worked my StateTree class into the new StateGraph class (which
is what it originally should have been named anyway). Basically, now I build
a graph of states, with a node for each state and an edge for each touch/clap
state change (StateGraph#build & StateGraph#build_recurse). This graph is then
repeatedly traversed, so that definite outcomes propagate through it (each
state has a best_outcome attached to it) (StateGraph#propagate_outcomes). Once
no more changes are made, the propagation is complete. Then each node's
best_outcome contains the best outcome for the player whose turn it is at the
node.

So if the root is, say, Outcome::P2Win, then player 2 is guaranteed a
winning strategy. (Another change from my first submission: Outcome::Loop has
been replaced with Outcome::Unknown, which all states default to at first. If
they are still unknown after executing StateGraph#propagate_outcomes, looping
is the best they can do.

The code is longer than I'd like, and there's some definite redundancy and
ugliness in there, but I've spent far too long debugging this to feel like
cleaning it up now. Speaking of debugging, it became quite a chore to verify
that my program was working right, so I wrote a little script to turn a
StateGraph into input for the graph-drawing program GraphViz. This did add
to the bloat, but I think it was worth it, since being able to look at the
graphs made things much easier. By setting FingersPerHand to 2, I generated
the attached f2.png image. That's not very impressive, but you can see the
next step up here (somewhat large):

http://www.jessemerriman.com/images/ruby_quiz/120_magic_fingers/f3.png

For higher values its impractical, both for the size of the generated image,
and the running time of the image generator. In the images, yellow nodes are
loop states, green are states that lead to player 1 winning, dark green are
actual player 1 win nodes, red are states that lead to player 2 winning, and
magenta are actual player 2 win nodes. The asterisks show whose turn it is.
Touch edges are solid and black, while clap edges are dashed and gray.

Using grep & wc -l on my dot files, I came up with the following stats:

FingersPerHand   #Nodes   #Edges
--------------   ------   -------
       2            4         3
       3           46        64
       4          156       316
       5          382      1000
       6          784      2444

(any higher than 6 and I get stack overflows).

One thing I could have done differently was not count whose turn it is in the
state, and just reverse the outcome or not depending on whose it is. But then
I'd have to store an array of reachable outcomes in each state, instead of just
the single best one for its player.

The diff between my original submission and my new code would be pretty long,
so I'm just going to paste it all in here:

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# constants.rb
# Ruby Quiz 120: Magic Fingers

Left,    Right   = 0, 1 # hand array indices
Player1, Player2 = 0, 1 # player array indices
FingersPerHand   = 5    # on my machine, > 6 causes a stack overflow

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# outcome.rb
# Ruby Quiz 120: Magic Fingers

require 'constants'

module Outcome
  # Order important: from best-for-player-1 to best-for-player-2
  Outcome::P1Win   = 0
  Outcome::Unknown = 1
  Outcome::P2Win   = 2

  # Given an Enumerable of outcomes, return the one that is best for the given
  # player.
  def Outcome.best(player, outcomes)
    best = (player == Player1) ? outcomes.min : outcomes.max
    best.nil? ? Outcome::Unknown : best
  end
end

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# state.rb
# Ruby Quiz 120: Magic Fingers

require 'constants'
require 'outcome'
require 'set'

# Represents one state of the game, which includes how many fingers are on each
# player's hands, and whose turn it is. States can also have parents, and a
# best_outcome can be assigned to them, though this class doesn't do anything
# with that itself. The player's hands are always sorted, since it doesn't
# matter having 3-left and 2-right is equivalent to 2-left and 3-right. When
# comparing states with ==, eql?, or their hashes, only @players and @turn are
# taken into account.
class State
  attr_reader :players, :turn, :parent, :best_outcome
  attr_writer :best_outcome

  # player_1 and player_2 are Arrays of number-of-fingers on each hand.
  def initialize(player_1, player_2, turn, parent = nil)
    @players = [player_1, player_2]
    @turn = turn
    @parent = parent
    @touch_reachable = @clap_reachable = nil

    for player in @players do
      State.normalize(player)
    end

    if end_state?
      @best_outcome = (self.winner == Player1 ? Outcome::P1Win : Outcome::P2Win)
    else
      @best_outcome = Outcome::Unknown
    end

    self
  end

  def hand_alive?(player_num, hand_num)
    @players[player_num][hand_num] > 0
  end

  def player_alive?(player_num)
    hand_alive?(player_num, Left) or hand_alive?(player_num, Right)
  end

  # true if at least one player is dead.
  def end_state?
    not player_alive?(Player1) or not player_alive?(Player2)
  end

  # Return the winner. This should only be called on end states (otherwise,
  # it'll always return Player1).
  def winner
    player_alive?(Player1) ? Player1 : Player2
  end

  # Turn the given player's hand into a fist if it has >= FingesPerHand
  # fingers up, and sort the hands.
  def State.normalize(player)
    for hand_num in [Left, Right] do
      player[hand_num] = 0 if player[hand_num] >= FingersPerHand
    end
    player.sort!
  end

  # Return a nice string representation of a player.
  def player_string(player_num)
    player = @players[player_num]
    '-' * (FingersPerHand-player[Left]) +
      '|' * player[Left] +
      '  ' +
      '|' * player[Right] +
      '-' * (FingersPerHand-player[Right])
  end

  # Return a nice string representation of this state (including both player
  # strings).
  def to_s
    s = "1: #{player_string(Player1)}"
    s << ' *' if @turn == Player1
    s << "\n2: #{player_string(Player2)}"
    s << ' *' if @turn == Player2
    s
  end

  # Return a compact string representation.
  def to_compact_s
    if @turn == Player1
      "[#{@players[Player1].join(',')}]* [#{@players[Player2].join(',')}]"
    else
      "[#{@players[Player1].join(',')}] [#{@players[Player2].join(',')}]*"
    end
  end

  # Equality only tests the players' hands and the turn.
  def ==(other)
    @players == other.players and @turn == other.turn
  end

  # Both eql? and hash are defined so that Sets/Hashes of states will only
  # differentiate states based on @players and @turn.
  def eql?(other); self == other; end
  def hash; [@players, @turn].hash; end

  # Yield once for each ancestor state, starting from the oldest and ending on
  # this state.
  def each_ancestor
    ancestors = [self]
    while not ancestors.last.parent.nil?
      ancestors << ancestors.last.parent
    end
    ancestors.reverse_each { |a| yield a }
  end

  # Have one player (the toucher) touch the other player (the touchee).
  def State.touch(toucher, toucher_hand, touchee, touchee_hand)
    touchee[touchee_hand] += toucher[toucher_hand]
  end

  # Yield each state reachable from this state by a touch move.
  def each_touch_reachable_state
    if @touch_reachable.nil?
      # Set to avoid duplicates.
      @touch_reachable = Set[]

      player = @players[@turn]
      opponent_num = (@turn + 1) % 2
      opponent = @players[opponent_num]

      for player_hand in [Left, Right] do
        for opponent_hand in [Left, Right] do
          if hand_alive?(@turn, player_hand) and
              hand_alive?(opponent_num, opponent_hand)
            op = opponent.clone # because touch modifies it
            State.touch(player, player_hand, op, opponent_hand)
            if @turn == Player1
              @touch_reachable << State.new(player, op, opponent_num, self)
            else
              @touch_reachable << State.new(op, player, opponent_num, self)
            end
          end
        end
      end
    end

    @touch_reachable.each { |r| yield r }
  end

  # Yield each state reachable from this state by a clap move.
  def each_clap_reachable_state
    if @clap_reachable.nil?
    # Set to avoid duplicates.
      @clap_reachable = Set[]
      player = @players[@turn]
      opponent_num = (@turn + 1) % 2
      opponent = @players[opponent_num]

      # Clap rules.
      for source_hand in [Left, Right] do
        target_hand = (source_hand == Left ? Right : Left)
        # The first line is the number that can be removed from the source.
        # The second is the number that can be added to the target without
        # killing it.
        max_transfer = [player[source_hand],
                      (FingersPerHand - player[target_hand] - 1)].min
        (1..max_transfer).each do |i|
          # skip transfers that just flip the hands
          next if (player[source_hand] - i) == player[target_hand]

          p = player.clone
          p[source_hand] -= i
          p[target_hand] += i
          if @turn == Player1
            @clap_reachable << State.new(p, opponent.clone, opponent_num, self)
          else
            @clap_reachable << State.new(opponent.clone, p, opponent_num, self)
          end
        end
      end
    end

    @clap_reachable.each { |r| yield r }
  end

  # Yield once for each state reachable from this one.
  def each_reachable_state
    each_touch_reachable_state { |r| yield r }
    each_clap_reachable_state  { |r| yield r }
  end
end

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# state_graph.rb
# Ruby Quiz 120: Magic Fingers

require 'constants'
require 'outcome'
require 'state'
require 'set'

class Set
  # Get an object in the Set which equals (by ==) obj.
  def get(obj)
    find { |x| x == obj }
  end
end

# Represents a tree of States in parent-child relationships.
class StateGraph
  attr_reader :root, :edges

  def initialize(root = State.new([1,1], [1,1], Player1))
    @root = root
    @node_clap_children = {} # Maps States to Arrays of their clapped children.
    @node_touch_children = {} # Maps States to Arrays of their touched children.
    build
    self
  end

  # Traverse the graph from the root, filling in @node_*_children.
  def build
    @seen_nodes = Set[]
    build_recurse(@root)
    remove_instance_variable :@seen_nodes
  end

  # Traverse the graph from the given node, filling in @node_*_children.
  def build_recurse(node)
    @seen_nodes << node
    @node_clap_children[node]  = [] if not @node_clap_children.has_key? node
    @node_touch_children[node] = [] if not @node_touch_children.has_key? node

    # Here we have to be careful to not re-create nodes that are equal to
    # previously created nodes. This is why I added Set#get above.
    node.each_clap_reachable_state do |reached|
      if @seen_nodes.include? reached
        @node_clap_children[node] << @seen_nodes.get(reached)
      else
        @node_clap_children[node] << reached
        build_recurse(reached)
      end
    end
    node.each_touch_reachable_state do |reached|
      if @seen_nodes.include? reached
        @node_touch_children[node] << @seen_nodes.get(reached)
      else
        @node_touch_children[node] << reached
        build_recurse(reached)
      end
    end
  end

  # Call a Proc for every state encountered in the tree.
  # All procs should accept two arguments: the found state, and
  # the state from which it was found (nil for the root, may be different from
  # what the state reports as its parent, if there is more than one parent).
  def walk(new_clap_proc, new_touch_proc, old_clap_proc, old_touch_proc)
    @seen_nodes = Set[]
    new_touch_proc[@root, nil]
    walk_recurse(@root, new_clap_proc, new_touch_proc,
                        old_clap_proc, old_touch_proc)
    remove_instance_variable :@seen_nodes
  end

  def walk_recurse(node, new_clap_proc, new_touch_proc,
                         old_clap_proc, old_touch_proc)
    @seen_nodes << node

    @node_clap_children[node].each do |reached|
      if @seen_nodes.include?(reached)
        old_clap_proc[reached, node]
      else
        new_clap_proc[reached, node]
        walk_recurse(reached, new_clap_proc, new_touch_proc,
                              old_clap_proc, old_touch_proc)
      end
    end

    @node_touch_children[node].each do |reached|
      if @seen_nodes.include?(reached)
        old_touch_proc[reached, node]
      else
        new_touch_proc[reached, node]
        walk_recurse(reached, new_clap_proc, new_touch_proc,
                              old_clap_proc, old_touch_proc)
      end
    end
  end

  # Yield for each node in the graph.
  def each_node
    # Can use either @node_clap_children or @node_touch_children here.
    @node_clap_children.each_key { |node| yield node }
  end

  # Yield for every child of the given node.
  def each_child(node)
    @node_clap_children[node].each  { |child| yield child }
    @node_touch_children[node].each { |child| yield child }
  end

  # Starting from the root, pull up all outcomes that are absolutely determined
  # given perfect play. Eg, say we have a state, S. S can move to a win state
  # for player 1. If its player 1's turn in S, then S's best_outcome will be
  # set to Outcome::P1Win. The graph will be scanned from the root repeatedly
  # until no more changes can be made to let these outcomes propagate. If
  # complete is set to true, outcome propagation will be completely determined
  # for all states in the graph. If its false, only those that affect the root
  # will be determined. This is faster if you only care about knowing root's
  # outcome.
  def pull_up_outcomes(complete = true)
    begin
      @seen_nodes = Set[]
      @changed = false
      if complete
        each_node { |node| pull_up_recurse(node) }
      else
        pull_up_recurse(@root)
      end
    end while @changed
    remove_instance_variable :@seen_nodes
    remove_instance_variable :@changed
  end

  def pull_up_recurse(node)
    @seen_nodes << node

    if node.best_outcome == Outcome::Unknown
      reachable_outcomes = Set[]

      each_child(node) do |reached|
        if @seen_nodes.include? reached
          reachable_outcomes << reached.best_outcome
        else
          reachable_outcomes << pull_up_recurse(reached)
        end
      end

      best = Outcome.best(node.turn, reachable_outcomes)
      if best != node.best_outcome
        node.best_outcome = best
        @changed = true
      end
    end

    node.best_outcome
  end
end

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# game.rb
# Ruby Quiz 120: Magic Fingers

require 'constants'
require 'outcome'
require 'state'
require 'state_graph'

class Game
  # Constructor. p1_start and p2_start are Arrays containing the number of
  # fingers on each of their hands at the start of the game.
  def initialize(p1_start = [1,1], p2_start = [1,1])
    @graph = StateGraph.new(State.new(p1_start, p2_start, Player1))
    self
  end

  # Print out an analysis of the game.
  def analyze
    @graph.pull_up_outcomes
    outcome = @graph.root.best_outcome

    if outcome == Outcome::P1Win
      puts 'Player 1, with perfect play, can win.'
    elsif outcome == Outcome::P2Win
      puts 'No matter how well Player 1 plays, Player 2 can win.'
    elsif outcome == Outcome::Unknown
      puts 'Perfect play by both players leads to a loop.'
    else
      puts 'I am broken.'
    end
  end
end

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# magic_fingers.rb
# Ruby Quiz 120: Magic Fingers

require 'game'

# Note: These examples assume FingersPerHand == 5.

Game.new.analyze # Normal, default game.
#Game.new([1,1], [0,4]).analyze # P1 should win on move 1.
#Game.new([4,4], [1,1]).analyze # P1 should win on move 2.
#Game.new([0,1], [3,3]).analyze # P2 should win on move 2.

# -------------------------------------------------------------------
#!/usr/bin/env ruby
# state_graph_to_graphviz.rb
# Ruby Quiz 120: Magic Fingers

# This script outputs to stdout a dot file for use with GraphViz.
# ./state_graph_to_graphviz.rb | dot -Tpng -o output.png

require 'state_graph'

GraphVizHeader = "digraph F#{FingersPerHand} {\nmargin=0.2"
GraphVizFooter = '}'

# Node with best_outcome == Outcome::Unknown.
DefaultNodeOpts  = '[width=1.5,shape=oval,style=filled,fillcolor=lightyellow]'
# Node with best_outcome == Outcome::P1Win.
P1WinParentNodeOpts = '[width=1.5,shape=oval,style=filled,fillcolor=green]'
# Node with best_outcome == Outcome::P2Win.
P2WinParentNodeOpts = '[width=1.5,shape=box,style=filled,fillcolor=red]'
# End state node where player 1 wins.
P1WinnerNodeOpts = '[width=2.2,shape=box,style=filled,fillcolor=darkgreen]'
# End state node where player 2 wins.
P2WinnerNodeOpts = '[width=2.2,shape=box,style=filled,fillcolor=magenta]'
# Edge for state --clap--> state.
ClapEdgeOpts     = '[style="dashed",color=gray]'
# Edge for state --touch-> state. 
TouchEdgeOpts    = '[style="solid",color=black]'

# Only for use with non-end-states.
OutcomesToOpts = {
  Outcome::P1Win   => P1WinParentNodeOpts,
  Outcome::P2Win   => P2WinParentNodeOpts,
  Outcome::Unknown => DefaultNodeOpts
}

def node_opts(node)
  if node.end_state?
    node.winner == Player1 ? P1WinnerNodeOpts : P2WinnerNodeOpts
  else
    OutcomesToOpts[node.best_outcome]
  end
end

def name(state)
  state.end_state? ? 'End: ' + state.to_compact_s : state.to_compact_s
end

# Adds a newly clapped state to the graph.
new_clap = lambda do |state, parent|
  name = name(state)

  puts "\"#{name}\" #{node_opts(state)};"
  if not parent.nil?
    puts "\"#{name(parent)}\" -> \"#{name}\" #{ClapEdgeOpts};"
  end
end

# Adds a newly touched state to the graph.
new_touch = lambda do |state, parent|
  name = name(state)

  puts "\"#{name}\" #{node_opts(state)};"
  if not parent.nil?
    puts "\"#{name(parent)}\" -> \"#{name}\" #{TouchEdgeOpts};"
  end
end

# Adds a clap edge to an old state to the graph.
old_clap = lambda do |state, parent|
  puts "\"#{name(parent)}\" -> \"#{name(state)}\" #{ClapEdgeOpts}"
end

# Adds a touch edge to an old state to the graph.
old_touch = lambda do |state, parent|
  puts "\"#{name(parent)}\" -> \"#{name(state)}\" #{TouchEdgeOpts}"
end

puts GraphVizHeader
# Create the initial state tree, with only end states' outcomes known.
graph = StateGraph.new
# Pull up all outcomes.
graph.pull_up_outcomes
# Walk the tree and generate GraphViz input.
graph.walk(new_clap, new_touch, old_clap, old_touch)
puts GraphVizFooter
# -------------------------------------------------------------------

-- 
Jesse Merriman
jessemerriman@warpmail.net
http://www.jessemerriman.com/

Attachments (1)

f2.png (1.29 KB)
f2.png

In This Thread

Prev Next