hennesse has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I'm working up to writing a Perl Freecell Solver - for no particular reason since it's been done a hundred times before, and I'm sure better than I could ever do. I'm just trying to keep my 65-year-old brain active. I'm about 3/4 of the way through writing a command-line player. Complicated! Jeez, what was I thinking?

My current problem is writing a Freecell Dealer. There's some pretty good code click here, but it produces some weird 4-character Unicode results.

The first character is always a Greek Gamma, the second is always a o-umlaut or o-diaeresis (or maybe it's a Greek Omicron?). The third character encodes the card's suit: u-acute = hearts, a-underscore(?) = diamonds, capital Enya = clubs, a-acute = spades. The fourth character is the correct rank: A=ace, T=10, J=Jack, Q=Queen, K=King. So it's working correctly - but I don't like the Unicode results.

How can I modify it so that the output is more "normal" - 9C = Nine of Clubs, JD = Jack of Diamonds, etc.

No sense reinventing the wheel. Just want to make the wheel round.

P.S. The code author chose game 11982 - the Impossible game - to illustrate his slightly obfuscated code. Run it with the parameter "1" on the command line, and you should get what's at the top of the linked page.

Thanks
Dave Hennessey

Replies are listed 'Best First'.
Re: Freecell Dealer
by Athanasius (Archbishop) on Jul 25, 2018 at 05:59 UTC

    Hello hennesse,

    I was able to get the output shown at the top of the linked page by making these changes to line 17:

    # push @d, map("$_$b", qw/♣ ♦ ♥ ♠/);    # original
      push @d, map("$b$_", qw/C D H S/);    # new
    #               ^^^^      ^ ^ ^ ^

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Ave Athanasius

      Thanks. I tried the obvious, but didn't think to reverse the first argument. Always amazing how the smallest problem can consume the largest amount of time. Now I'm back to frying the bigger fish.

      Dave

      I don't see any reason to get rid of those cool unicode chars. The first output was the original, and for some reason, it doesn't look like it's on top of another character like it does in the terminal, but I definitely saw a problem with legibility on the terminal. The second output is with your new line 17. The ultimate output is with the unicode chars stuffed back into the qw list, and it displays fine for me. Followed by source listing:

       
      
      
      $ ./1.fc.pl
      Hand 11982
      ♥A ♠A ♥4 ♣A ♦2 ♠6 ♠T ♠J
      ♦3 ♥3 ♠Q ♣Q ♠8 ♥7 ♦A ♠K
      ♦K ♥6 ♠5 ♦4 ♥9 ♥J ♠9 ♣3
      ♣J ♦5 ♣5 ♣8 ♦9 ♦T ♥K ♣7
      ♣6 ♣2 ♥T ♥Q ♦6 ♣T ♠4 ♠7
      ♦J ♦7 ♥8 ♣9 ♥2 ♦Q ♣4 ♥5
      ♣K ♦8 ♠2 ♠3
      $ ./1.fc.pl
      Hand 11982
      AH AS 4H AC 2D 6S TS JS
      3D 3H QS QC 8S 7H AD KS
      KD 6H 5S 4D 9H JH 9S 3C
      JC 5D 5C 8C 9D TD KH 7C
      6C 2C TH QH 6D TC 4S 7S
      JD 7D 8H 9C 2H QD 4C 5H
      KC 8D 2S 3S
      $ ./1.fc.pl
      Hand 11982
      A♥ A♠ 4♥ A♣ 2♦ 6♠ T♠ J♠
      3♦ 3♥ Q♠ Q♣ 8♠ 7♥ A♦ K♠
      K♦ 6♥ 5♠ 4♦ 9♥ J♥ 9♠ 3♣
      J♣ 5♦ 5♣ 8♣ 9♦ T♦ K♥ 7♣
      6♣ 2♣ T♥ Q♥ 6♦ T♣ 4♠ 7♠
      J♦ 7♦ 8♥ 9♣ 2♥ Q♦ 4♣ 5♥
      K♣ 8♦ 2♠ 3♠
      $ cat 1.fc.pl 
      #!/usr/bin/perl
       
      use strict;
      use warnings;
       
      use utf8;
       
      sub deal {
          my $s = shift;
       
          my $rnd = sub {
              return (($s = ($s * 214013 + 2531011) & 0x7fffffff) >> 16 );
          };
       
          my @d;
          for my $b (split "", "A23456789TJQK") {
                push @d, map("$b$_", qw/♣ ♦ ♥ ♠/);    # newest with unicode chars
          }
       
          for my $idx (reverse 0 .. $#d) {
              my $r = $rnd->() % ($idx + 1);
              @d$r, $idx = @d$idx, $r;
          }
       
          return reverse @d;
      }
       
      my $hand_idx = shift(@ARGV) // 11_982;
       
      my $cards = deal($hand_idx);
       
      my $num_cards_in_height = 8;
      my $string = '';
       
      while (@$cards)
      {
          $string .= join(' ', splice(@$cards, 0, 8)) . "\n";
      }
       
      binmode STDOUT, ':encoding(utf-8)';
      print "Hand $hand_idx\n";
      print $string;
      $ 
       

      I think the bigger criticism at this time is that there doesn't seem to be a stochastic component. Maybe space out the columns a bit, so the eye can track them down....

Re: Freecell Dealer
by Anonymous Monk on Jul 25, 2018 at 07:18 UTC
    I understand that I'm not answering your question (which is answered above), but I have an explanation for this behaviour:
    The first character is always a Greek Gamma, the second is always a o-umlaut or o-diaeresis (or maybe it's a Greek Omicron?). The third character encodes the card's suit: u-acute = hearts, a-underscore(?) = diamonds, capital Enya = clubs, a-acute = spades.
    This happens when UTF-8-encoded bytes get interpreted as CP437-encoded bytes:
    $ echo '♣ ♦ ♥ ♠' | iconv -f cp437 # my system is UTF-8
    ΓÖú ΓÖª ΓÖÑ ΓÖá
    The author of the code assumed that your command line would understand UTF-8, but you are probably running the code in Windows cmd.exe, which doesn't. According to Wikipedia, you may still see those characters if you print qq{\x{03}\x{04}\x{05}\x{06}}, although they are not formally defined as part of the code page.
Re: Freecell Dealer
by Anonymous Monk on Jul 25, 2018 at 09:20 UTC

    You could also insert this somewhere in your program:

    my $cp; BEGIN { ($cp) = `chcp & chcp 65001` =~ /(\d+)/ if $^O eq 'MSWin32' } END { `chcp $cp` if $^O eq 'MSWin32' }