------art_2860_15554074.1107881501231
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

I've enclosed my solution to the "Solving Tactics" quiz as an attached
gzipped tar file. The README from that file is reproduced, below.

Enjoy!
Bob

This is my solution to the "Solving Tactics" Ruby Quiz #18.

I previously solved this puzzled over 30 years ago on a Control Data
Corporation mainframe computer (A CDC Cyber 6400). The program was
hand-optimized assembler code, written as a self-assigned term
project. My best recollection is that it took a few seconds to run;
probably on the order of 10 seconds. Interestingly, my Ruby version
takes a similar amount of time on my fairly ancient 600MHz processor.

The basic idea is simple: a Tactics board contains just 16 cells,
which can be empty or filled. This readily admits to a representation
as a 16-bit number. Games can be played by using bit operations to
determine and play individual moves. The key observation is that there
are just 2**16 possible board positions. Each of these is either a
winning or losing position.

This solution implements a playing engine that can play from any given
board position. The algorithm is to see whether, from that position, a
win is ensured, or not. This is done by trying all the possible moves
from that position until a win is found. If a win is found, then the
starting position is marked as a losing position (because the next
play could win). Otherwise, if no win's are possible for the opponent
from this position, then the starting position is marked as a win. A
play is trivially determined to be a win or loss if has been marked as
such from a previous play. If that result has not been cached, then
the play engine is invoked recursively. Eventually, the algorithm will
terminate, because the cache is seeded with the information that an
entirely filled grid represents a loss. To determine whether the first
or second player is bound to win, simply invoke the play engine with
an empty board.

This takes a lot more effort to say in words, than to program in
code. The actual code described above is just this:

  # Play from the current position. If *any* move guarantees a win,
  # then mark this position as a WIN and return it. Otherwise this
  # position loses.
  def play
    @possible_moves.each do |move|
      new_board  board | move
      if (@@position[new_board] || Tactics.new(new_board,
@possible_moves).play) LOSS then
        return @@position[@board]  IN 
      end
    end
    @@position[@board]  OSS
  end

The required result is determined by this separately provided program:

require 'tactics'
puts %(#{Tactics.new.play Tactics::WIN ? "First" : "Second"} player wins.)

The Quiz writeup suggests that a good solution should demonstrate
convincingly that the answer given likely to be correct. I've chosen
to use unit tests for this purpose. The unit tests take advantage of
the play engine's ability to play from any board position to play a
variety of end games. In each case a proof is provided (in the
comments) of which player should win or lose, and the unit test
asserts that the play engine agrees with the proof's assertion. This
is a form of black-box testing that doesn't directly test (for
example) that the program even knows how to play every possible
move. But, indirectly, it provides strong evidence that this is likely
to be the case.

Bob.Sidebotham / gmail.com

------art_2860_15554074.1107881501231
Content-Type: application/x-tar; name="tactics.tar.gz"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="tactics.tar.gz"

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
d+yO3bE7quO/6N9XnABQAAA------art_2860_15554074.1107881501231--