Demonstrating Perl with Tic-Tac-Toe, Part 4

This is the final article to the series demonstrating Perl with Tic-Tac-Toe. This article provides a module that can compute better game moves than the previously presented modules. For fun, the modules chip1.pm through chip3.pm can be incrementally moved out of the hal subdirectory in reverse order. With each chip that is removed, the game will become easier to play. The game must be restarted each time a chip is removed.

An example Perl program

Copy and paste the below code into a plain text file and use the same one-liner that was provided in the the first article of this series to strip the leading numbers. Name the version without the line numbers chip3.pm and move it into the hal subdirectory. Use the version of the game that was provided in the second article so that the below chip will automatically load when placed in the hal subdirectory. Be sure to also include both chip1.pm and chip2.pm from the second and third articles, respectively, in the hal subdirectory.

00 # artificial intelligence chip
01 
02 package chip3;
03 require chip2;
04 require chip1;
05 
06 use strict;
07 use warnings;
08 
09 sub moverama {
10    my $game = shift;
11    my @nums = $game =~ /[1-9]/g;
12    my $rama = qr/[1973]/;
13    my %best;
14 
15    for (@nums) {
16       my $ra = $_;
17       next unless $ra =~ $rama;
18       $best{$ra} = 0;
19       for (@nums) {
20          my $ma = $_;
21          next unless $ma =~ $rama;
22          if (($ra-$ma)*(10-$ra-$ma)) {
23             $best{$ra} += 1;
24          }
25       }
26    }
27 
28    @nums = sort { $best{$b} <=> $best{$a} } keys %best;
29 
30    return $nums[0];
31 }
32 
33 sub hal_move {
34    my $game = shift;
35    my $mark = shift;
36    my @mark = @{ shift; };
37    my $move;
38 
39    $move = chip2::win_move $game, $mark, \@mark;
40 
41    if (not defined $move) {
42       $mark = ($mark eq $mark[0]) ? $mark[1] : $mark[0];
43       $move = chip2::win_move $game, $mark, \@mark;
44    }
45 
46    if (not defined $move) {
47       $move = moverama $game;
48    }
49 
50    if (not defined $move) {
51       $move = chip1::hal_move $game;
52    }
53 
54    return $move;
55 }
56 
57 sub complain {
58    print 'Just what do you think you\'re doing, ',
59    ((getpwnam($ENV{'USER'}))[6]||$ENV{'USER'}) =~ s! .*!!r, "?\n";
60 }
61 
62 sub import {
63    no strict;
64    no warnings;
65 
66    my $p = __PACKAGE__;
67    my $c = caller;
68 
69    *{ $c . '::hal_move' } = \&{ $p . '::hal_move' };
70    *{ $c . '::complain' } = \&{ $p . '::complain' };
71 
72    if (&::MARKS->[0] ne &::HAL9K) {
73       @{ &::MARKS } = reverse @{ &::MARKS };
74    }
75 }
76 
77 1;

How it works

Rather than making a random move or making a move based on probability, this final module to the Perl Tic-Tac-Toe game uses a more deterministic algorithm to calculate the best move.

The big takeaway from this Perl module is that it is yet another example of how references can be misused or abused, and as a consequence lead to unexpected program behavior. With the addition of this chip, the computer learns to cheat. Can you figure out how it is cheating? Hints:

  1. Constants are implemented as subroutines.
  2. References allow data to be modified out of scope.

Final notes

Line 12 demonstrates that a regular expression can be pre-compiled and stored in a scalar for later use. This is useful as performance optimization when you intend to re-use the same regular expression many times over.

Line 59 demonstrates that some system library calls are available directly in Perl’s built-in core functionality. Using the built-in functions alleviates some overhead that would otherwise be required to launch an external program and setup the I/O channels to communicate with it.

Lines 72 and 73 demonstrate the use of &:: as a shorthand for &main::.

The full source code for this Perl game can be cloned from the git repository available here: https://pagure.io/tic-tac-toe.git

For Developers For System Administrators

5 Comments

  1. Possible convert!

    Nice series of articles. Perl 7 was just released. Interesting community https://www.perlfoundation.org/

  2. orest

    change my mind: python is superior to perl in every single aspect

    • That’s fine. To each their own, as they say.

      I’m not really trying to persuade anyone to use Perl. These articles are meant to be informational. They explain what Perl is about and demonstrate how it works for anyone who might be interested in dabbling with it.

      I agree that Python is an easier language and that Perl is not for the faint of heart.

    • Bill Chatfield

      I don’t hate Python. It is very easy to read. But, it is not the greatest language ever. Significant white space is stupid. In Python you have to import everything that Perl provides by default. Python is way too slow. Perl is much faster. Java is even faster than Perl. Python might be faster than Ruby but that isn’t saying much. Write some test programs and time them. You can prove it to yourself very easily. Neither Python nor Perl are good for large programs because they’re not statically typed. You need Java or C/C++ for that. Python or Perl are both great for smaller programs. Here’s some more reasons why Python isn’t the greatest language:

      http://www.hackerfactor.com/blog/index.php?/archives/825-8-Reasons-Python-Sucks.html
      https://medium.com/@natemurthy/all-the-things-i-hate-about-python-5c5ff5fda95e

      • Arnaud

        And that’s without talking about heresy like the side effects of mutables on passing arguments as object references… I think the worst is that it is defended as a feature by the Python community.

Comments are Closed

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions