# One Die Game (#203)

**URL:** <https://www.ruby-forum.com/t/one-die-game-203/166980>\
**Category:** Ruby\
**Created:** [May 1, 2009, 5:48pm UTC](https://www.ruby-forum.com/t/one-die-game-203/166980 "2009-05-01T17:48:15Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 1, 2009, 5:48pm UTC](https://www.ruby-forum.com/t/one-die-game-203/166980/1 "2009-05-01T17:48:15Z")

</div>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Q.:

1. Please do not post any solutions or spoiler discussion for this  
quiz until 48 hours have elapsed from the time this message was  
sent.

2. Support Ruby Q. by submitting ideas and responses  
as often as you can!  
Visit: [http://rubyquiz.strd6.com/suggestions](http://rubyquiz.strd6.com/suggestions)

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem  
helps everyone on Ruby T. follow the discussion. Please reply to  
the original quiz message, if you can.

RSS Feed: [http://rubyquiz.strd6.com/quizzes.rss](http://rubyquiz.strd6.com/quizzes.rss)

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## One Die Game (#203)

Hej Rubyists,

This week’s quiz was submitted by Siep K. through the  
suggestions page [http://rubyquiz.strd6.com/suggestions](http://rubyquiz.strd6.com/suggestions).

Imagine a two player game with the following rules:

- Playing material: 1 ordinary die; opposing sides sum up to 7.
- The first move consists of the first player rolling the die and  
initializing the total to the amount on the face shown.
- All further moves consist of turning the die one quarter and adding  
the face value of the new side to the current total. Example: If the  
die is facing 2 after the initial throw, then possible moves are 1,3,4  
and 6, with totals 3,5,6 and 8.
- A player wins when that player’s move results in a total of 31.
- A player loses when that player overshoots 31.

Let’s say it’s your turn. The current total is 24 and the die is  
facing 3. You can win by turning the die to face 6, totaling 30. Your  
opponent cannot play 1 because it’s at the bottom of the die, so he is  
forced to overshoot the goal of 31.

Build a program which plays this game against a human opponent and  
blunders occasionally (to keep it fun).

P.S. I did not invent this game; I’m pretty sure I found this game in  
a chapter about nim-like games[1](http://en.wikipedia.org/wiki/Nim) in one of Martin Gardner’s books.

Have Fun!

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 6, 2009, 11:43pm UTC](https://www.ruby-forum.com/t/one-die-game-203/166980/2 "2009-05-06T23:43:11Z")

</div>

Why do I feel like I’m doing someone’s homework assignment ?

Here’s my attempt at the quiz:  
[http://www.pastie.org/469635](http://www.pastie.org/469635)

I know Daniel asked that the computer sometimes make a mistake (for  
fun), but my computer is not really into having fun. It’s a type-a,  
aggressive, winner takes all computer. Don’t mess with it.

I’m throwing the gauntlet down. Write you own computer opponent and we  
can have them battle it out!

Subclass Player:  
class MyRadAI \< Player

Create a method next move:  
def next\_move(dice, opponents)

opponents is an array of the other players and each opponent has name  
and score accessors.  
the dice class has some convenient methods available to you to figure  
out which moves are available:  
def available\_nums  
def valid?(choice) is also useful.

Luke

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 7, 2009, 8:44pm UTC](https://www.ruby-forum.com/t/one-die-game-203/166980/3 "2009-05-07T20:44:45Z")

</div>

My AI is simple, but I think it is still pretty playable.

-C

class Game  
class Player  
attr\_reader :name  
def initialize(die,name)  
@die,@name = die,name  
end  
def choose\_move(score)  
gets.to\_i  
end  
end

class AI \< Player  
def choose\_move(score)  
sleep(1)  
if score \< 25  
while !@die.legal\_move?( (r = rand(6)+1) )  
end  
return r  
else  
#simple win  
return 31-score if @die.legal\_move?(31-score)  
return rand(6)+1  
end  
end  
end

class Die  
attr\_reader :current\_side  
def initialize  
roll  
@opposites = [[1,6],[2,5],[3,4]]  
end  
def roll  
@current\_side = rand(6)+1  
end  
def legal\_move?(side)  
return false if side == @current\_side  
return false if side \> 6 or side \< 1  
a = [@current\_side,side].sort!  
return !@opposites.include?(a)  
end  
def flip(side)  
if legal\_move?(side)  
@current\_side = side  
0  
else  
1  
end  
end  
private :roll  
end  
def initialize  
print "enter your name: "  
name = gets.chomp  
@d = Die.new  
@bust, @score = 31,0  
@players = [AI.new(@d,“the computer”),Player.new(@d,name)]  
@players = rand(10) \< 5 ? @players : @players.reverse  
puts “#{@players.last.name} rolled the die to  
#{@d.current\_side}\n\n\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*”  
@score += @d.current\_side  
end  
def update  
@players.each do |p|  
puts “current score = #{@score}”  
while @d.flip(p.choose\_move(@score)) == 1 ? true : false  
puts “invalid move”  
end  
puts “#{p.name} flipped the die to  
#{@d.current\_side}\n\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*”  
@score += @d.current\_side  
if @score \> @bust  
puts “#{p.name} lost!”  
return false  
end  
if @score == @bust  
puts “#{p.name} won!”  
return false  
end  
end  
return true  
end  
def play  
while update  
end  
end  
private :update  
end

g = Game.new  
g.play

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 12, 2009, 12:42am UTC](https://www.ruby-forum.com/t/one-die-game-203/166980/4 "2009-05-12T00:42:18Z")

</div>

Luke C. and Chris Cacciatore both submitted solutions for this  
week’s quiz. There was some ambiguity in the way the quiz was posted,  
so the implementations were slightly different from one another. Luke  
had a running total for each player and Chris had a single total.  
Despite that difference, both solutions tackled the problem similarly  
and are both interesting.

The `Dice` classes in both solutions have methods for rolling a random  
side and validating moves by keeping track of opposites. Chris keeps  
an array of explicitly defined opposites: `@opposites = [[1,6],[2,5],[3,4]]`. Luke’s method for determining available moves  
makes use of the fact that opposites are defined by the faces adding  
up to seven:

def available\_nums  
(1…@total\_sides).reject {|n| (@top + n == (@total\_sides + 1) || n  
== @top)}  
end

Each of these implementations has a different strength when it comes  
to future changes: Chris’s is easier to rearrange the configuration of  
the dice and Luke’s is easier to expand to dice with a different  
number of faces. One caveat for dice of more faces is that additional  
adjacency information would need to be added (for example a  
dodecahedron or icosahedron would have different adjacencies).

Both solutions require the definition of only one method to create a  
new player or different AI. For Luke’s code the only method that needs  
to be implemented is `next_move`. Let’s take a look at the `Human` and  
`Computer` `Player` implementations:

class Human \< Player  
def next\_move(dice, opponents = nil)  
done = false  
while(done == false)  
choice = gets.to\_i  
if(dice.valid?(choice) == true)  
done = true  
else  
print “!!!you can’t choose #{choice}!!!”  
end  
puts  
end

```
choice

```

end  
end

For the human player it gets the choice from the prompt, prompting  
again if the player selects an invalid choice.

class Computer \< Player  
def next\_move(dice, opponents = nil)  
if(@score == 30)  
return 1  
elsif((@score % 6) == 0) && dice.available\_nums.include?(6)  
return 6  
else  
return dice.available\_nums[rand(4)]  
end  
end  
end

The computer player tries to set up a path for a win. If the score is  
a multiple of 6 and the computer is able to choose 6 on the die, then  
it will choose 6 each time until the score reaches 30, and then will  
choose 1 on the next turn to secure victory. If it can’t then it makes  
a random choice. It would be fairly easy to implement different  
computer AI by providing different ways to compute the next move.

Chris uses a similar technique for choosing the next move from the  
computer player. It checks for a winning move and takes it if  
available, otherwise it makes a random move.

def choose\_move(score)  
sleep(1)  
if score \< 25  
while !@die.legal\_move?( (r = rand(6)+1) )  
end  
return r  
else  
#simple win  
return 31-score if @die.legal\_move?(31-score)  
return rand(6)+1  
end  
end

The play consists of players alternating moves. Both programs check  
for the validity of the move outside of the player’s implementation,  
so you are unable to cheat by having your player return a roll of `(31

- current\_value)`.

An interesting element of Luke’s program is that he allows you to set  
up a game between two human players or two computer players rather  
than just between a human and a computer.

Though the specific game mechanics were slightly different in each  
program, they both provided interesting solutions to the problem.

Thank you Luke and Chris for your submissions to this week’s quiz!
