The game

Given an   N×N   square array of zeroes or ones in an initial configuration,   and a target configuration of zeroes and ones.



The game is to transform one to the other in as few moves as possible by inverting whole numbered rows or whole lettered
columns at once   (as one move).

In an inversion.   any  1  becomes  0,   and any  0  becomes  1  for that whole row or column.



Task

Create a program to score for the Flipping bits game.

 The game should create an original random target configuration and a starting configuration.
 Ensure that the starting position is never the target position.
 The target position must be guaranteed as reachable from the starting position.   (One possible way to do this is to generate the start position by legal flips from a random target position.   The flips will always be reversible back to the target from the given start position).
 The number of moves taken so far should be shown.


Show an example of a short game here, on this page, for a   3×3   array of bits.




package require Tcl 8.6
 
oo::class create Flip {
    variable board target s
    constructor {size} {
	set s $size
	set target [my RandomConfiguration]
	set board $target
	while {$board eq $target} {
	    for {set i 0} {$i < $s} {incr i} {
		if {rand()<.5} {
		    my SwapRow $i
		}
		if {rand()<.5} {
		    my SwapColumn $i
		}
	    }
	}
    }
 
    method RandomConfiguration {{p 0.5}} {
	for {set row 0} {$row < $s} {incr row} {
	    set r {}
	    for {set col 0} {$col < $s} {incr col} {
		lappend r [expr {rand() < $p}]
	    }
	    lappend result $r
	}
	return $result
    }
 
    method SwapRow {rowId} {
	for {set i 0} {$i < $s} {incr i} {
	    lset board $rowId $i [expr {![lindex $board $rowId $i]}]
	}
    }
    method SwapColumn {columnId} {
	for {set i 0} {$i < $s} {incr i} {
	    lset board $i $columnId [expr {![lindex $board $i $columnId]}]
	}
    }
 
    method Render {configuration {prefixes {}}} {
	join [lmap r $configuration p $prefixes {
	    format %s%s $p [join [lmap c $r {string index ".X" $c}] ""]
	}] "\n"
    }
    method GetInput {prompt} {
	puts -nonewline "${prompt}: "
	flush stdout
	gets stdin
    }
 
    method play {} {
	set p0 {}
	set p {}
	set top [format "%*s " [string length $s] ""]
	for {set i 1;set j 97} {$i<=$s} {incr i;incr j} {
	    append top [format %c $j]
	    lappend p [format "%*d " [string length $s] $i]
	    lappend p0 [format "%*s " [string length $s] ""]
	}
 
	set moves 0
	puts "You are trying to get to:\n[my Render $target $p0]\n"
	while true {
	    puts "Current configuration (#$moves):\n$top\n[my Render $board $p]"
 
	    # Test for if we've won
	    if {$board eq $target} break
 
	    # Ask the user for a move
	    set i [my GetInput "Pick a column (letter) or row (number) to flip"]
 
	    # Parse the move and apply it
	    if {[string is lower -strict $i] && [set c [expr {[scan $i "%c"] - 97}]]<$s} {
		my SwapColumn $c
		incr moves
	    } elseif {[string is integer -strict $i] && $i>0 && $i<=$s} {
		my SwapRow [expr {$i - 1}]
		incr moves
	    } else {
		puts "Error: bad selection"
	    }
	    puts ""
	}
	puts "\nYou win! (You took $moves moves.)"
    }
}
 
Flip create flip 3
flip play
 