home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / 8puzzle.zip / 8PUZZLE.PAS < prev    next >
Pascal/Delphi Source File  |  1988-10-25  |  14KB  |  663 lines

  1. program puzzle8;
  2.  
  3. {
  4.   Copyright Norman Newman, Kibbutz Mishmar David, Israel.
  5.   This Turbo Pascal version was successfully ported
  6.   from the PDP version on 4 July 1987.
  7.  
  8.   This is a more Turbo-like version, March 1988.
  9.   Updated to Turbo-4, October 1988.
  10.  
  11.   Permission is granted to use this program, or portions thereof,
  12.   for non-commercial purposes. All other rights are reserved to
  13.   the original author.
  14.  
  15. }
  16. uses dos, crt;
  17.  
  18. const
  19.  version = 9;
  20.  zero = 48; { ASCII ord('0') }
  21.  goal = '1234 5678';
  22.  hash_max = 1008;
  23.  hash_max_plus = 1009;
  24.  
  25. type
  26.  square = packed array [1..9] of char;
  27.  
  28. var
  29.  original, onscreen: square;
  30.  hash_table: array [0..hash_max] of square;
  31.  table: array [1..81] of integer;
  32.  preint: array [1..9] of integer;
  33.  prech: array ['1'..'8'] of char;
  34.  moves: array [1..9,1..5] of integer;
  35.  compcount, humcount: integer;
  36.  complay, humplay: boolean;
  37.  ch: char;
  38.  video_mode: byte;
  39.  regs: registers;
  40.  
  41. {****************************************************}
  42.  
  43.  function get_mode: byte;
  44.   { return our current video mode }
  45.   begin
  46.    regs.ax:= $0F00;
  47.    intr ($10, regs);
  48.    get_mode:= regs.al
  49.   end;
  50.  
  51.  procedure set_mode (mode: byte);
  52.   { set the video mode }
  53.   begin
  54.    regs.ah:= 0;
  55.    regs.al:= mode;
  56.    intr ($10, regs);
  57.   end;
  58.  
  59.  function inkey: integer;
  60.   { this function returns the code of the key pressed in the low
  61.     byte.
  62.     If the high byte is 0, an extended code was read;
  63.     if the high byte is 1, an ordinary key was read
  64.   }
  65.   begin
  66.    regs.ah:= 7; { read character without echo }
  67.    msdos (regs);
  68.    if regs.al > 0 { ordinary key }
  69.     then regs.ah:= 1
  70.     else
  71.      begin { get rest of key code }
  72.       msdos (regs);
  73.       regs.ah:= 0
  74.      end;
  75.    inkey:= regs.ax
  76.   end { inkey };
  77.  
  78.  function evaluate (var p:square): integer;
  79.   var
  80.    i, tmp: integer;
  81.    ch: char;
  82.    blank: boolean;
  83.  
  84.   begin
  85.    tmp:= 0;
  86.    i:= 0;
  87.    while i < 9 do
  88.     begin
  89.      i:= i + 1;
  90.      ch:= p[i];
  91.      blank:= ch = ' ';
  92.      if not blank
  93.       then tmp:= tmp + table[(i-1)*9 + ord(ch) - zero]
  94.       else tmp:= tmp + table[i*9];
  95.  
  96.      if blank
  97.       then if i <> 5 then tmp:= tmp + 2
  98.                      else
  99.       else
  100.        case i of
  101.          5:;
  102.          2,4,6,8:
  103.           if p[5] <> ' '
  104.            then if (p[preint[i]] <> prech[ch])
  105.                    and (ch <> prech[p[5]])
  106.                    then tmp:= tmp + 5
  107.                    else
  108.           else
  109.            if p[preint[i]] <> prech[ch]
  110.             then tmp:= tmp + 5;
  111.          1,3,7,9:
  112.           if p[preint[i]] <> prech[ch]
  113.            then tmp:= tmp + 3
  114.        end
  115.     end;
  116.    evaluate:= tmp
  117.  end { evaluate };
  118.  
  119. {***********************************************}
  120.  
  121.  procedure print_square (var p: square);
  122.   var
  123.    i,j: integer;
  124.  
  125.   begin
  126.    for i:= 1 to 3 do
  127.     begin
  128.      if p[i] <> onscreen[i]
  129.       then
  130.        begin
  131.         gotoxy(i+i+17,10);
  132.         write (p[i]);
  133.         onscreen[i]:= p[i]
  134.        end;
  135.  
  136.     j:= i + 3;
  137.     if p[j] <> onscreen[j]
  138.      then
  139.       begin
  140.        gotoxy(i+i+17,12);
  141.        write (p[j]);
  142.        onscreen[j]:= p[j]
  143.       end;
  144.  
  145.     j:= j + 3;
  146.     if p[j] <> onscreen[j]
  147.      then
  148.       begin
  149.        gotoxy(i+i+17,14);
  150.        write (p[j]);
  151.        onscreen[j]:= p[j]
  152.       end
  153.     end;
  154.    delay (25);
  155.   end { print_square };
  156.  
  157. {***********************************************}
  158.  
  159.  procedure initialise;
  160.  
  161.   procedure init_eval;
  162.    var
  163.     a,b,c: packed array [1..27] of char;
  164.     i: byte;
  165.  
  166.    begin
  167.     a:= '012132342101223321210314322';
  168.     b:= '123021231212112120321203211';
  169.     c:= '234130122323221011432312102';
  170.     for i:= 1 to 27 do
  171.      begin
  172.       table[i]:= ord(a[i]) - zero;
  173.       table[i+27]:= ord(b[i]) - zero;
  174.       table[i+54]:= ord(c[i]) - zero
  175.      end;
  176.  
  177.    preint[1]:= 4; preint[2]:= 1; preint[3]:= 2;
  178.    preint[4]:= 7; preint[5]:= 5; preint[6]:= 3;
  179.    preint[7]:= 8; preint[8]:= 9; preint[9]:= 6;
  180.  
  181.    prech['1']:= '4'; prech['2']:= '1'; prech['3']:= '2';
  182.    prech['4']:= '6'; prech['5']:= '3'; prech['6']:= '7';
  183.    prech['7']:= '8'; prech['8']:= '5';
  184.  
  185.   end { init_eval };
  186.  
  187.   procedure initmov;
  188.    var
  189.     i,j: byte;
  190.     tab: packed array [1..45] of char;
  191.  
  192.    begin
  193.     tab:= '224003153022600315704246833590248003759026800';
  194.     for i:= 1 to 9 do
  195.      for j:= 1 to 5 do
  196.       moves[i,j]:= ord(tab[(i-1)*5+j]) - zero
  197.    end { initmov };
  198.  
  199.   procedure init_square;
  200.    var
  201.     i: integer;
  202.     ch: char;
  203.  
  204.    procedure random_entry;
  205.     var
  206.      i,hole, new_hole: integer;
  207.  
  208.     begin
  209.      randomize;
  210.      original:= goal;
  211.      hole:= 5;
  212.      for i:= 1 to 500 do
  213.       begin
  214.        new_hole:= random(moves[hole,1]) + 1;
  215.        new_hole:= moves[hole,new_hole + 1];
  216.        original[hole]:= original[new_hole];
  217.        original[new_hole]:= ' ';
  218.        hole:= new_hole
  219.       end
  220.     end { random entry };
  221.  
  222.   procedure debug_entry;
  223.    var
  224.     i: byte;
  225.     key: integer;
  226.  
  227.    begin
  228.     gotoxy(1,14);
  229.     for i:= 1 to 9 do
  230.      begin
  231.       write ('Square #':15, i:1, ' ? ');
  232.       repeat
  233.        key:= inkey
  234.       until (hi(key) = 1) and (lo(key) in [32, 49..56]);
  235.       original[i]:= chr(key);
  236.       writeln (chr(key))
  237.      end;
  238.    end { debug_entry };
  239.  
  240.    begin { init_square }
  241.     gotoxy (10,13);
  242.     write ('<D>ebug or <R>andom ? ');
  243.     ch:= readkey;
  244.     if (ch = 'd') or (ch = 'D')
  245.      then debug_entry
  246.      else random_entry;
  247.     gotoxy (1,13);
  248.     clreol
  249.    end { init_square };
  250.  
  251.   procedure init_frame;
  252.    var
  253.     i,j: byte;
  254.  
  255.     procedure line;
  256.      begin
  257.       write (chr(186), chr(186):2, chr(186):2, chr(186):2)
  258.      end;
  259.  
  260.     procedure join;
  261.      begin
  262.       write (chr(204), chr(205), chr(206), chr(205),
  263.              chr(206), chr(205), chr(185));
  264.      end;
  265.  
  266.    begin
  267.     fillchar (onscreen, 9, ' ');
  268.     highvideo;
  269.     gotoxy (18,9);
  270.     { top line }
  271.     write (chr(201),chr(205), chr(203), chr(205),
  272.            chr(203), chr(205), chr(187));
  273.     gotoxy(18,10); line;
  274.     gotoxy(18,11); join;
  275.     gotoxy(18,12); line;
  276.     gotoxy(18,13); join;
  277.     gotoxy(18,14); line;
  278.     { bottom line }
  279.     gotoxy(18,15);
  280.     write (chr(200), chr(205), chr(202), chr(205), chr(202),
  281.            chr(205), chr(188));
  282.     normvideo;
  283.    end { init_frame };
  284.  
  285.   begin { initialise }
  286.    init_eval;
  287.    initmov;
  288.    init_square;
  289.    init_frame;
  290.    fillchar (hash_table, hash_max_plus*9, 'a')
  291.   end;
  292.  
  293. {***********************************************}
  294.  
  295.  procedure human;
  296.   var
  297.    sq: square;
  298.    your_move, hole, i: integer;
  299.    flag: boolean;
  300.  
  301.   function legal : boolean;
  302.    begin
  303.     case hole of
  304.      1: legal:= your_move > 0;
  305.      2: legal:= your_move <> -3;
  306.      3: legal:= (your_move = -1) or (your_move = 3);
  307.      4: legal:= your_move <> -1;
  308.      5: legal:= true;
  309.      6: legal:= your_move <> 1;
  310.      7: legal:= (your_move = -3) or (your_move = 1);
  311.      8: legal:= your_move <> 3;
  312.      9: legal:= your_move < 0;
  313.      else legal:= true
  314.     end
  315.    end { legal };
  316.  
  317.   begin { human }
  318.    sq:= original;
  319.    gotoxy(1,19);
  320.    writeln ('Use the arrow keys to move the hole');
  321.    write ('F10 to quit');
  322.    clreol;
  323.    gotoxy(1,24);
  324.    write('Moves so far - ');
  325.  
  326.    while sq <> goal do
  327.     begin
  328.      hole:= 1;
  329.      while sq[hole] <> ' ' do hole:= hole + 1;
  330.      repeat
  331.       gotoxy(1,22);
  332.       write('Which way do you want to move the hole? ');
  333.  
  334.       case inkey of
  335.        72: your_move:= -3;
  336.        75: your_move:= -1;
  337.        80: your_move:= 3;
  338.        77: your_move:= 1;
  339.        68: your_move:= 4; { finish }
  340.        else your_move:= 5 { illegal }
  341.       end
  342.     until legal;
  343.  
  344.     if your_move = 5 then { do nothing }
  345.     else if your_move = 4
  346.      then
  347.       begin
  348.        humplay:= false;
  349.        humcount:= 0;
  350.        sq:= goal { force an end }
  351.       end
  352.     else if legal
  353.      then
  354.       begin
  355.        sq[hole]:= sq[hole + your_move];
  356.        sq[hole + your_move]:= ' ';
  357.        print_square(sq);
  358.        humcount:= humcount + 1;
  359.        gotoxy(16,24);
  360.        write (humcount)
  361.       end
  362.    end
  363.   end;
  364.  
  365. {***********************************************}
  366.  
  367.  procedure computer;
  368.   label
  369.    999;
  370.  
  371.   type
  372.    node = ^node_type;
  373.    node_type = record
  374.                 index: 0..hash_max;
  375.                 score, hole: integer;
  376.                 parent, next: node
  377.                end;
  378.  
  379.   var
  380.    head, n, son, free: node;
  381.    i, inc: integer;
  382.    finished: boolean;
  383.  
  384.   procedure insert (var head: node; son: node);
  385.    var
  386.     front, rear: node;
  387.     count: integer;
  388.     duplicate: boolean;
  389.  
  390.     procedure attach (head: node);
  391.      begin
  392.       if front = nil
  393.        then front:= head
  394.        else rear^.next:= head;
  395.       rear:= head
  396.     end { attach };
  397.  
  398.    begin { insert }
  399.     duplicate:= false;
  400.     if son^.score < head^.score
  401.      then
  402.       begin
  403.        son^.next:= head;
  404.        head:= son
  405.       end
  406.      else
  407.       begin
  408.        front:= nil;
  409.        count:= 0;
  410.        while son^.score >= head^.score do
  411.         begin
  412.          duplicate:= son^.index = head^.index;
  413.          attach (head);
  414.          head:= head^.next;
  415.          count:= count + 1
  416.         end;
  417.  
  418.        if not duplicate then duplicate:= count > 20;
  419.        if duplicate
  420.         then attach (head)
  421.         else
  422.          begin
  423.           son^.next:= head;
  424.           attach (son)
  425.          end;
  426.        head:= front
  427.       end
  428.    end { insert };
  429.  
  430.   function hash (var sq: square): integer;
  431.   { returns -1 if sq is not a new square,
  432.     else returns the hash value, and as a side effect,
  433.     the square is entered into the hash table }
  434.  
  435.    var
  436.     first, found: boolean;
  437.     h, acc, i: integer;
  438.  
  439.    begin
  440.     h:= 0;
  441.     for i:= 1 to 4 do
  442.      begin
  443.       acc:= 10 * ord(sq[i]) + ord(sq[i+4]);
  444.       h:= (10*h + acc) mod hash_max_plus
  445.      end;
  446.     h:= (h + ord(sq[9])) mod hash_max_plus;
  447.  
  448.     found:= false;
  449.     repeat
  450.      if hash_table[h,1] = 'a'
  451.       then
  452.        begin
  453.         found:= true;
  454.         first:= true;
  455.         hash_table[h]:= sq
  456.        end
  457.      else if hash_table[h] = sq
  458.       then
  459.        begin
  460.         found:= true;
  461.         first:= false
  462.        end
  463.      else h:= (h + 63) mod hash_max_plus ;
  464.     until found;
  465.  
  466.     if first
  467.      then hash:= h
  468.      else hash:= -1
  469.    end { hash };
  470.  
  471.   function makenode (father: node; i: integer): node;
  472.    var
  473.     switch, space: integer;
  474.     h: integer;
  475.     sq: square;
  476.     n: node;
  477.  
  478.    begin
  479.     with father^ do
  480.      begin
  481.       space:= hole;
  482.       sq:= hash_table[index]
  483.      end;
  484.     switch:= moves[space,i+1];
  485.     if i > moves[space,1]
  486.      then makenode:= nil
  487.      else
  488.       begin
  489.        sq[space]:= sq[switch];
  490.        sq[switch]:= ' ';
  491.        h:= hash(sq);
  492.        if h >= 0
  493.         then
  494.          begin
  495.           new(n);
  496.           with n^ do
  497.            begin
  498.             index:= h;
  499.             hole:= switch;
  500.             score:= evaluate(sq) + inc;
  501.             parent:= father;
  502.             next:= nil
  503.            end;
  504.           makenode:= n
  505.          end
  506.         else makenode:= nil;
  507.        end
  508.     end { makenode };
  509.  
  510.  begin { computer }
  511.   gotoxy (1,20);
  512.   write ('Give me a moment while I solve ');
  513.   gotoxy (1,21);
  514.   write ('this puzzle ... ');
  515.   clreol;
  516.   new (head);
  517.   with head^ do
  518.    begin
  519.     index:= hash(original);
  520.     hole:= 1;
  521.     while original[hole] <> ' ' do hole:= hole + 1;
  522.     score:= evaluate (original);
  523.     parent:= nil;
  524.     new (next);
  525.     with next^ do
  526.      begin
  527.       score:= maxint;
  528.       next:= nil
  529.      end
  530.    end;
  531.  
  532.   finished:= original = goal;
  533.   inc:= 0;
  534.   while not finished do
  535.    begin
  536.     n:= head;
  537.     head:= head^.next;
  538.     inc:= inc + 1;
  539.     i:= 0;
  540.     while (i < 4) and not finished do
  541.      begin
  542.       if inc > 500 then goto 999;
  543.       i:= i + 1;
  544.       son:= makenode(n,i);
  545.       if son <> nil
  546.        then
  547.         begin
  548.          insert (head, son);
  549.          finished:= hash_table[son^.index] = goal
  550.         end
  551.        end;
  552.      end;
  553.  
  554. 999:
  555.   if not finished
  556.    then
  557.     begin
  558.      gotoxy (1,20);
  559.      write ('Sorry to have wasted your time, ');
  560.      gotoxy(1,21);
  561.      write ('but that puzzle seems unsolvable');
  562.      complay:= false
  563.     end
  564.    else
  565.     begin
  566.      son^.next:= nil;
  567.      head:= son;
  568.      while son^.parent <> nil do
  569.       begin
  570.        son:= son^.parent;
  571.        son^.next:= head;
  572.        head:= son
  573.       end;
  574.  
  575.    compcount:= 0;
  576.    print_square (original);
  577.    while head <> nil do
  578.     with head^ do
  579.      begin
  580.       head:= next;
  581.       print_square(hash_table[index]);
  582.       compcount:= compcount + 1;
  583.       delay (50);
  584.      end;
  585.  
  586.    gotoxy (1,22);
  587.    write ('The computer finished in ');
  588.    write (compcount:1, ' moves');
  589.    clreol;
  590.   end
  591.  end { computer };
  592.  
  593. {***********************************************}
  594.  
  595. begin { main program }
  596.  clrscr;
  597.  video_mode:= get_mode;
  598.  if video_mode <> 7 then set_mode (0);
  599.  gotoxy(10,3);
  600.  highvideo;
  601.  write ('WELCOME TO THE 8 PUZZLE');
  602.  normvideo;
  603.  gotoxy (18,5);
  604.  write ('Version ', version:1);
  605.  initialise;
  606.  print_square (original);
  607.  
  608.  gotoxy(1,20);
  609.  write ('Do you want to try <y/n>? ');
  610.  ch:= chr(inkey);
  611.  if (ch = 'y') or (ch = 'Y')
  612.   then
  613.    begin
  614.     humplay:= true;
  615.     humcount:= 0;
  616.     human
  617.    end
  618.   else humplay:= false;
  619.  
  620.  if humplay
  621.   then
  622.    begin
  623.     gotoxy (1,17);
  624.     write ('Your moves - ', humcount)
  625.    end;
  626.  
  627.  gotoxy (1,19); clreol;
  628.  gotoxy (1,22); clreol;
  629.  gotoxy (1,24); clreol;
  630.  gotoxy (1,20);
  631.  write ('Do you want the computer  ');
  632.  gotoxy (2,21);
  633.  write ('to solve the puzzle <y/n>? ');
  634.  ch:= chr(inkey);
  635.  if (ch = 'y') or (ch = 'Y')
  636.   then
  637.    begin
  638.     print_square (original);
  639.     complay:= true;
  640.     computer
  641.    end
  642.   else complay:= false;
  643.  
  644.  if complay
  645.   then
  646.    begin
  647.     gotoxy (18,17);
  648.     write ('My moves - ', compcount)
  649.    end;
  650.  
  651.  gotoxy(1,23);
  652.  if humplay and complay
  653.   then if humcount < compcount
  654.    then write ('You beat the computer!')
  655.    else if humcount = compcount
  656.     then write ('We came out equal that time')
  657.     else write ('Better luck next time');
  658.  clreol;
  659.  gotoxy(1,24);
  660.  write ('Press any key to finish ... ');
  661.  compcount:= inkey;
  662.  if video_mode <> 7 then set_mode (video_mode);
  663. end.