Peter’s solutions: week 104 — 15 March 2021

THE WEEKLY CHALLENGE
FUSC and NIM

The Perl Camel

Task 2

Nim game

Write a script to simulate the Nim Game. It is played between 2 players. For the purpose of this task, assume you play against the machine.

There are 3 simple rules to follow:

  • You have 12 tokens
  • Each player can pick 1, 2 or 3 tokens at a time
  • The player who picks the last token wins the game

Analysis

I've coded this in Perl with output in HTML. This means that the screen is reloaded for each play: a trivial matter in today's world, but if I were doing this for real I'd probably code the guts in JScript so that only one page load was needed per game.

The logic is quite simple and the machine behaves fairly predictably, although there is a random element.

Try it and see if you can win!

Try it 

Play the Game of Nim

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2021-03-15
use utf8;     # Week 104 - task 2 - Nim game
use warnings; # Peter Campbell Smith
use Encode;
require './pwc_utils.pl';

our ($base, %data, $date, $root);
my ($s, @colours, $col, $gone, $e, $tokens, $start, $scores, $starts, $my_take, $messages);
    
# standard start
pwc_start();
$messages = '';

# page heading
page_begin({title => 'The Weekly Challenge', description => 'The Weekly Challenge', styles => '',
    heading => qq[The Weekly Challenge], line1 => 'The game of Nim'});

# top stuff
say qq[<div style='margin:0 1em 0 1em'>
    <div style='float:right;width:20em' class='this'>
    <p style='text-align:right; margin-top:1em'>
    <a href='/challenge'>Current week &rarr;</a></div>
    <div style='clear:both'></div>];
    
# left column
say qq[<div style='float:left; width:49%; min-width:300px;'>
    <p><form action='?' method='post'>];

# restart game or series
$data{news} = 1 unless defined $data{tokens};
$starts = 1 - $data{starts} if $data{newg};
if ($data{newg} or $data{news}) {
    $tokens = 12;
    if ($data{news}) {
        $scores = qq[0v0];
    } else {
        $scores = $data{scores};
    }
    $messages .= '' . ($starts == 1 ? '<p>Your' : '<b>My') . ' turn to start';
} else {
    $starts = $data{starts};
    $tokens = $data{tokens};
    $scores = $data{scores};
}

# apply your play
if ($data{take} =~ m|(\d)|) {
    $tokens -= $1;
    $messages .= qq[<p>You took $1];
}

if ($tokens == 0) {
    $messages .= qq[<p style='color:green'>Well done, you win!];
    $scores =~ m|(\d+)v(\d+)|;
    $scores = $1 . 'v' . ($2 + 1);
    
# make my play (unless it's you to start)
} else {
    unless ($starts == 1 and $tokens == 12) {
                     # 1  2  3  4  5  6  7  8  9 10 11  
        $my_take = (0, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1)[$tokens];
        $my_take = int(rand(3)) + 1 if $tokens == 12;
        $tokens -= $my_take;
        $messages .= qq[<p>I've taken $my_take];
    }

    if ($tokens <= 0) {
        $messages .= qq[<p style='color:red'>Bad luck, you lose!];
        $scores =~ m|(\d+)v(\d+)|;
        $scores = ($1 + 1) . qq[v$2];
    }
}
# scoreboard
$scores =~ m|(\d+)v(\d+)|;
say qq[<table style='border:0; width:80%; text-align:center; font-size:20pt; 
    color:white; font-weight:bold; background:gray;'>
    <tr><td colspan=2>SCOREBOARD</td></tr>
    <tr><td>YOU</td><td>ME</td></tr>
    <tr><td>$2</td><td>$1</td></tr></table>];

say qq[<span style='font-weight:bold'><br>$messages</span>] if $messages;

# show tokens
say qq[<p><span style='font-size:30pt;font-weight:bold'>];
for $s (0 .. 11) {
    $col = $s <= $tokens - 1 ? 'red' : 'silver';
    print qq[<span style='color:$col'>&#] . ($s + 9312) . ' </span>';
    say q[<br>] if $s == 5;
    say qq[</span>] if $s == 11;
}
    
# invite play
if ($tokens > 0) {  
    say qq[<h2>Choose your move</h2>    
        <p><input name='take' type='submit' value='Remove 1' style='width:10em'>];
    say qq[<p><input name='take' type='submit' value='Remove 2' style='width:10em'>] if $tokens >= 2;
    say qq[<p><input name='take' type='submit' value='Remove 3' style='width:10em'>] if $tokens >= 3;

# game over
} else {
    say qq[<p><input name='newg' type='submit' value='New game' style='width:10em'>];
}
say qq[<br>&nbsp;</div>];
    
# right column
say qq[<div style='float:left; width:49%; min-width:300px;'>
    <h2>Rules</h2>
    <ul><li>Game starts with 12 tokens
    <li>Play alternates between you and me
    <li>Each play removes 1, 2 or 3 tokens
    <li>Person removing last token <b>wins!</b></ul>
        
    <h2>Options</h2>
    <p><input name='news' type='submit' value='Clear scoreboard' style='width:10em'>
    <p><b><a href='/challenge/104/2'>Go back to challenge</a></b>
    </div>
    
    <input type='hidden' name='tokens' value='$tokens'>];

# data pass on
say qq[<input type='hidden' name='starts' value='$starts'>
    <input type='hidden' name='scores' value='$scores'>
    <input type='hidden' name='tokens' value='$tokens'>
    </form></div>];

page_end();

88 lines of code
Completed after the closing date and not submitted to GitHub

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain