Skip to content

Instantly share code, notes, and snippets.

@mberends
Created September 25, 2011 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mberends/1240895 to your computer and use it in GitHub Desktop.
Save mberends/1240895 to your computer and use it in GitHub Desktop.
GUI solution for masak's matchboxes puzzle on Niecza Perl 6
# gtk-matchboxes.pl - GUI solution for http://irclog.perlgeek.de/perl6/2011-09-23#i_4469618
constant $GTK = "gtk-sharp,Version=2.12.0.0,Culture=neutral,PublicKeyToken=35e10195dab3c99f";
constant $GDK = "gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f";
constant $GLIB = "glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f";
# use 'gacutil -l' to look up similar module details
constant Application = CLR::("Gtk.Application,$GTK");
constant GdkCairoHelper = CLR::("Gdk.CairoHelper,$GDK");
constant GLibTimeout = CLR::("GLib.Timeout,$GLIB");
constant GtkDrawingArea = CLR::("Gtk.DrawingArea,$GTK");
constant Window = CLR::("Gtk.Window,$GTK");
Application.Init;
my $window = Window.new("matchboxes");
my $running = True;
my $gameCount = 0;
constant InitialMatches = 40;
my @matchesA = 0 xx (InitialMatches+1);
my @matchesB = 0 xx (InitialMatches+1);
my $windowSizeX = 600; my $windowSizeY = 500;
$window.Resize($windowSizeX, $windowSizeY);
my $drawingarea = GtkDrawingArea.new;
$drawingarea.add_ExposeEvent(&ExposeEvent);
GLibTimeout.Add( 1000, &TimeoutEvent);
$window.add_DeleteEvent(&DeleteEvent);
$window.Add($drawingarea);
$window.ShowAll;
while $running { # event loop with custom body
Application.RunIteration(False); # handle all Gtk events
my ($box, $count) = MatchBoxGame(InitialMatches);
if $box eq 'A' { ++@matchesA[$count] } else { ++@matchesB[$count] }
++$gameCount;
}
# Randomly remove matches from box A or B until one of them is empty.
# Return the number of matches in the other box
sub MatchBoxGame($initialMatches)
{
my $matchesA = my $matchesB = $initialMatches;
while $matchesA > 0 && $matchesB > 0 {
if rand < 0.5 { --$matchesA; }
else { --$matchesB; }
}
return ($matchesA > 0) ?? ('A', $matchesA) !! ('B', $matchesB);
}
sub ExposeEvent($obj, $args) {
$args; # suppress 'declared but not used' warnings
my $cc = GdkCairoHelper.Create($obj.GdkWindow); # Cairo Context
# TODO: the following two lines pass parameters by value, need to pass by references to integers
# my $windowX; my $windowY; my $windowWidth; my $windowHeight; my $windowDepth;
# $obj.GdkWindow.GetGeometry($windowX, $windowY, $windowWidth, $windowHeight, $windowDepth);
# Tracked as https://github.com/sorear/niecza/issues/57
# TODO: remove the following one line cheat that works around the above problem
my $windowWidth = $windowSizeX; my $windowHeight = $windowSizeY;
$cc.SetSourceRGB(1, 1, 0.8); $cc.Paint(); # pale yellow background
DrawGraphs($cc, $windowWidth, $windowHeight);
$cc.Target.Dispose;
$cc.dispose-hack;
}
sub DrawGraphs($cc, $windowWidth, $windowHeight)
{
# Calculate dimensions relative to window size
my $lineWidth = (($windowWidth min $windowHeight) * 0.005).Int max 1;
my $axisTop = $windowHeight * 0.1;
my $axisBottom = $windowHeight * 0.9;
my $axisALeft = $windowWidth * 0.1;
my $axisARight = $windowWidth * 0.45;
my $axisBLeft = $windowWidth * 0.55;
my $axisBRight = $windowWidth * 0.9;
# Draw the histograms
# NYI [max] @matchesA, @matchesB;
my $maxMatches = 0;
for @matchesA -> $m { if $m > $maxMatches { $maxMatches = $m; } }
for @matchesB -> $m { if $m > $maxMatches { $maxMatches = $m; } }
my $scaleX = ($axisARight - $axisALeft) / InitialMatches;
my $scaleY = ($axisBottom - $axisTop) / $maxMatches;
$cc.SetSourceRGB(1, 0, 0); # red for box A
my $i; loop ($i = 1; $i <= InitialMatches; ++$i) {
my $height = @matchesA[$i] * $scaleY;
my $x0 = ($axisALeft + $i * $scaleX).Int;
my $top = $axisBottom - $height;
$cc.Rectangle($x0,$top,$scaleX-1,$height-1);
$cc.Fill;
}
$cc.SetSourceRGB(0, 1, 0); # green for box B
loop ($i = 1; $i <= InitialMatches; ++$i) {
my $height = @matchesB[$i] * $scaleY;
my $x0 = ($axisBLeft + $i * $scaleX).Int;
my $top = $axisBottom - $height;
$cc.Rectangle($x0,$top,$scaleX-1,$height-1);
$cc.Fill;
}
$cc.Stroke;
# Draw the horizontal and vertical axes
$cc.LineWidth = $lineWidth;
$cc.SetSourceRGB(0, 0, 0); # Black
$cc.MoveTo($axisALeft, $axisTop);
$cc.LineTo($axisALeft, $axisBottom);
$cc.LineTo($axisARight, $axisBottom);
$cc.MoveTo($axisBLeft, $axisTop);
$cc.LineTo($axisBLeft, $axisBottom);
$cc.LineTo($axisBRight, $axisBottom);
$cc.Stroke;
# Label the graphs
$cc.SetSourceRGB(0, 0, 0); # Black
$cc.SetFontSize($windowWidth * 0.1);
my $textWidth = $cc.TextExtents('A').Width;
my $textHeight = $cc.TextExtents('A').Height;
$cc.MoveTo($axisALeft + $textWidth*3, $axisTop+$textHeight); $cc.ShowText('A');
$cc.MoveTo($axisBLeft + $textWidth*3, $axisTop+$textHeight); $cc.ShowText('B');
$cc.SetFontSize($windowWidth * 0.05);
$textWidth = $cc.TextExtents('0').Width;
$textHeight = $cc.TextExtents('0').Height;
$cc.MoveTo($axisALeft - $textWidth*0.5, $axisBottom+$textHeight*1.5); $cc.ShowText('0');
$cc.MoveTo($axisARight - $textWidth, $axisBottom+$textHeight*1.5); $cc.ShowText('40');
$cc.MoveTo($axisBLeft - $textWidth*0.5, $axisBottom+$textHeight*1.5); $cc.ShowText('0');
$cc.MoveTo($axisBRight - $textWidth, $axisBottom+$textHeight*1.5); $cc.ShowText('40');
# Label the Y axis maximum
$cc.SetFontSize($windowWidth * 0.03);
$textWidth = $cc.TextExtents(~ $maxMatches).Width;
$textHeight = $cc.TextExtents(~ $maxMatches).Height;
$cc.MoveTo($axisALeft - $textWidth * 1.1, $axisTop+$textHeight*0.5);
$cc.ShowText(~ $maxMatches);
$cc.SetFontSize($windowWidth * 0.04);
# Put a heading above the graphs
my $heading = "$gameCount Matchbox games";
$textWidth = $cc.TextExtents($heading).Width;
$textHeight = $cc.TextExtents($heading).Height;
$cc.MoveTo($axisBLeft - $textWidth * 0.5, $axisTop - $textHeight * 0.5);
$cc.ShowText($heading);
$cc.Stroke;
}
sub TimeoutEvent # called once per second
{
$drawingarea.QueueDrawArea(0,0,$windowSizeX,$windowSizeY);
return True; # meaning please continue calling this timeout handler
};
sub DeleteEvent($obj, $args) {
$obj; $args; # suppress "declared but not used" Potential difficulties
$running = False;
Application.Quit;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment