Here's my tic-tac-toe program. I initially wrote it on the same day I posted the grid-click code; today I added the computer player. (Yes, I did get the technique of concatenating a line of squares from Darryl Tang's version.) #!perl -w use Mac::Events; use Mac::Windows; use Mac::QuickDraw; use Mac::Dialogs; sub ltwh ($$$$) { my ($left, $top, $width, $height) = @_; new Rect ($left, $top, $left+$width, $top+$height); } @Lines = ( [[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]], [[2, 0], [2, 1], [2, 2]], [[0, 0], [1, 0], [2, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2], [1, 2], [2, 2]], [[0, 0], [1, 1], [2, 2]], [[0, 2], [1, 1], [2, 0]], ); $Title = 'Tic-Tac-Toe'; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub userect { SetDialogItemProc($Q_Dlg->window, $_[0], sub { FrameRect $Q_Dlg->item_box($_[1]); }); } $Q_Dlg = new MacDialog ( ltwh(100, 90, 250, 170), 'Setup', 1, movableDBoxProc, 1, [kStaticTextDialogItem, ltwh(10, 10, 380, 16), 'Please select the player types:'], [kButtonDialogItem, ltwh(160, 140, 80, 20), 'Begin'], [kButtonDialogItem, ltwh( 60, 140, 80, 20), 'Quit'], [kRadioButtonDialogItem, ltwh(($w = 15)+8, 62, 80, 16), 'Human'], [kRadioButtonDialogItem, ltwh($w+8, 82, 80, 16), 'Computer'], [kUserDialogItem, ltwh($w, 50, 100, 56)], [kStaticTextDialogItem, ltwh($w+8, 42, 70, 16), '"X" Player'], [kRadioButtonDialogItem, ltwh(($w = 135)+8, 62, 80, 16), 'Human'], [kRadioButtonDialogItem, ltwh($w+8, 82, 80, 16), 'Computer'], [kUserDialogItem, ltwh($w, 50, 100, 56)], [kStaticTextDialogItem, ltwh($w+8, 42, 70, 16), '"O" Player'], ); $Q_OK = 0; $Q_Dlg->item_hit(2 => sub {$Q_OK = 1}); $Q_Dlg->item_hit(3 => sub {$Q_OK = -1}); SetDialogDefaultItem($Q_Dlg->window, 2); $Q_Dlg->item_hit(4 => sub {$IsComp{'x'} = 0; $Q_Dlg->item_value($_[1], 1); $Q_Dlg->item_value($_[1]+1, 0)}); $Q_Dlg->item_hit(5 => sub {$IsComp{'x'} = 1; $Q_Dlg->item_value($_[1], 1); $Q_Dlg->item_value($_[1]-1, 0)}); $Q_Dlg->item_value(4, 1); $IsComp{'x'} = 0; userect 6; $Q_Dlg->item_hit(8 => sub {$IsComp{o} = 0; $Q_Dlg->item_value($_[1], 1); $Q_Dlg->item_value($_[1]+1, 0)}); $Q_Dlg->item_hit(9 => sub {$IsComp{o} = 1; $Q_Dlg->item_value($_[1], 1); $Q_Dlg->item_value($_[1]-1, 0)}); $Q_Dlg->item_value(8, 1); $IsComp{'o'} = 0; userect 10; WaitNextEvent until $Q_OK; dispose $Q_Dlg; exit if $Q_OK == -1; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub Init { @Board = ( [' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' '], ); $Player = 'o'; $PWin = undef; $Clicks = 0; SetPort $win->window; InvalRect $win->window->portRect; SetWTitle $win->window, "$Title - @{[uc $Player]}'s turn"; 1; } sub automove { # This algorithm is NOT perfect. I thought about improving # it, but why not let the other player win occasionally? # The rand() is so that it will randomly select from # equal moves. my ($me) = @_; my @pmoves; my $him = $me eq 'o' ? 'x' : 'o'; $Board[1][1] eq ' ' and push @pmoves, [10 + rand, 1, 1]; foreach (@Lines) { my $line = $Board[$$_[0][0]][$$_[0][1]] . $Board[$$_[1][0]][$$_[1][1]] . $Board[$$_[2][0]][$$_[2][1]]; # Finish my lines $line eq " $me$me" and push @pmoves, [5 + rand, $$_[0][0], $$_[0][1]]; $line eq "$me $me" and push @pmoves, [5 + rand, $$_[1][0], $$_[1][1]]; $line eq "$me$me " and push @pmoves, [5 + rand, $$_[2][0], $$_[2][1]]; # Block opposing lines $line eq " $him$him" and push @pmoves, [2 + rand, $$_[0][0], $$_[0][1]]; $line eq "$him $him" and push @pmoves, [2 + rand, $$_[1][0], $$_[1][1]]; $line eq "$him$him " and push @pmoves, [2 + rand, $$_[2][0], $$_[2][1]]; # If nothing else, find empty space $line =~ /^ ..$/ and push @pmoves, [0 + rand, $$_[0][0], $$_[0][1]]; $line =~ /^. .$/ and push @pmoves, [0 + rand, $$_[1][0], $$_[1][1]]; $line =~ /^.. $/ and push @pmoves, [0 + rand, $$_[2][0], $$_[2][1]]; } die unless @pmoves; @pmoves = sort {$b->[0] <=> $a->[0]} @pmoves; return @{$pmoves[0]}[1..2]; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # $win = new MacWindow ( new Rect (100, 100, 300, 300), '', 1, documentProc, 1, ); Init(); $win->sethook(layout => sub { my $wRect = $win->window->portRect; $CWidth = int($wRect->right / 3); $CHeight = int($wRect->bottom / 3); SizeWindow $win->window, $CWidth * 3, $CHeight * 3; SetPort $win->window; InvalRect $wRect; }); $win->layout; $win->sethook(redraw => sub { PenSize(2, 2); RGBForeColor new RGBColor(0,0,0); MoveTo($CWidth - 1, 0); LineTo($CWidth - 1, $CHeight*3); MoveTo($CWidth*2 - 1, 0); LineTo($CWidth*2 - 1, $CHeight*3); MoveTo(0, $CHeight - 1); LineTo($CWidth*3, $CHeight - 1); MoveTo(0, $CHeight*2 - 1); LineTo($CWidth*3, $CHeight*2 - 1); PenSize(5, 5); for ($h = 0; $h < 3; $h++) { for ($v = 0; $v < 3; $v++) { my $p = $Board[$h][$v]; RGBForeColor($p !~ /[XO]/ ? new RGBColor(0,0,0) : new RGBColor(0,65535,0)); $p = lc $p; if ($p eq 'o') { FrameOval(new Rect( $h * $CWidth + $CWidth * .1, $v * $CHeight + $CHeight * .1, ($h+1) * $CWidth - $CWidth * .1, ($v+1) * $CHeight - $CHeight * .1, )); } elsif ($p eq 'x') { MoveTo( $h * $CWidth + $CWidth * .1, $v * $CHeight + $CHeight * .1, ); LineTo( ($h+1) * $CWidth - $CWidth * .1 - 5, ($v+1) * $CHeight - $CHeight * .1 - 5, ); MoveTo( ($h+1) * $CWidth - $CWidth * .1 - 5, $v * $CHeight + $CHeight * .1, ); LineTo( $h * $CWidth + $CWidth * .1, ($v+1) * $CHeight - $CHeight * .1 - 5, ); } } } }); $win->sethook(click => sub { my $pt = $_[1]; Init() and return if $PWin; my $gridh = int($pt->h/$CWidth); my $gridv = int($pt->v/$CHeight); return if $Board[$gridh][$gridv] ne ' '; my $r = new Rect( $gridh * $CWidth + ($CWidth * .1), $gridv * $CHeight + ($CWidth * .1), ($gridh+1) * $CWidth - ($CWidth * .1), ($gridv+1) * $CHeight - ($CWidth * .1) ); SetPort($win->window); my ($in, $oin) = (1, 1); InvertRect($r); while (StillDown()) { $in = PtInRect(GetMouse, $r); if ($in != $oin) { InvertRect($r); } $oin = $in; } return unless $in; move($gridh, $gridv); }); sub move { my ($gridh, $gridv) = @_; return if $Board[$gridh][$gridv] ne ' '; $Clicks++; $Board[$gridh][$gridv] = $Player; $Player = ($Player eq 'o') ? 'x' : 'o'; if ($Clicks >= 9) { SetWTitle $win->window, "$Title - Draw"; $PWin = -1; } else { SetWTitle $win->window, "$Title - @{[uc $Player]}'s turn"; } InvalRect new Rect( $gridh * $CWidth + ($CWidth * .1), $gridv * $CHeight + ($CWidth * .1), ($gridh+1) * $CWidth - ($CWidth * .1), ($gridv+1) * $CHeight - ($CWidth * .1) ); # Win check routine: foreach (@Lines) { next unless (my $p = $Board[$$_[0][0]][$$_[0][1]]) ne ' '; next unless $p eq $Board[$$_[1][0]][$$_[1][1]]; next unless $p eq $Board[$$_[2][0]][$$_[2][1]]; $PWin = $p; $Board[$$_[0][0]][$$_[0][1]] = uc $Board[$$_[0][0]][$$_[0][1]]; $Board[$$_[1][0]][$$_[1][1]] = uc $Board[$$_[1][0]][$$_[1][1]]; $Board[$$_[2][0]][$$_[2][1]] = uc $Board[$$_[2][0]][$$_[2][1]]; InvalRect $win->window->portRect; SetWTitle $win->window, "$Title - Won"; } } while ($win->window) { WaitNextEvent; if (!$PWin and $IsComp{$Player}) { move(automove($Player)); } } END {$win->dispose if $win} __END__ ==== Want to unsubscribe from this list? ==== Send mail with body "unsubscribe" to macperl-toolbox-request@macperl.org