Morpion solitaire/Unicon: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎Multigame: info on histogram and work of Jean-Jacques Sibil)
m (Fixed syntax highlighting.)
 
(33 intermediate revisions by one other user not shown)
Line 1: Line 1:
This example goes beyond the task goal of playing a random game of Morpion solitaire. The program was designed as a first cut/test bed more for understanding and exploring the problem space rather than for high efficiency. The program is structured to allow its behaviour to be configured (see options and M_ vars). Some of the features and extensions include:
This example goes beyond the task goal of playing a random game of Morpion solitaire. The code is divided into two sections (a) to support the basic task and (b) extensions. The program was designed as a first cut/test bed more for understanding and exploring the problem space rather than for high efficiency. The program is structured to allow its behaviour to be configured (see options and M_ vars). Some of the features and extensions include:
* Playing single games or running multigame simulations to find and capture the best games
* Playing single games or running multigame simulations to find and capture the best games
* Producing re-loadable game records and the ability to replay a saved game from a given position forward
* Producing re-loadable game records and the ability to replay a saved game from a given position forward
* The ability to reproduce purely random games from the random number seed
* The ability to reproduce purely random games from the random number seed
* Limiting the initial random moves to 1 of 4 cases (inside corners (1 type) x 8, outside corners (2 types) x 8, outside valley (1 type x 4)) not 1 of 28. In the case of this program these are all in the North East quadrant.
* Limiting the initial random moves to 1 of 4 cases (inside corners (1 type) x 8, outside corners (2 types) x 8, outside valley (1 type x 4)) not 1 of 28. In the case of this program these are all in the North East quadrant.
* Plays both 5T (by default) and 5D variants
* Plugable modules for functions including applying player strategy, position evaluation (fitness/heuristics)to facilitate quick experimentation
* Plugable modules for functions including applying player strategy, position evaluation (fitness/heuristics)to facilitate quick experimentation, changing game read/write functions, etc., etc.
* The version of the game played is a 'touching' (5T) game, although the validation procedure is easily parametrized.


Using random play in many summary runs, the highest (5T) score achieved was 90. While it was fairly easy to push random games into the low 80's running simulations of as little as a few hundred games, going beyond the highest score without adding some smarts will require some very long runs and is unlikely to result in any significant progress. Observing multigame runs show that play advances fairly quickly before a progress grinds to a virtual halt. Many runs peak in the first few hundred or thousand games.
Using random play in many summary runs, the highest (5T) score achieved was 92. While it was fairly easy to push random games into the low 80's running simulations of as little as a few hundred games, going beyond the highest score without adding some smarts will require some very long runs and is unlikely to result in any significant progress. Observing multigame runs show that play advances fairly quickly before progress grinds to a virtual halt. Many runs peak in the first few hundred or thousand games.


The ability to replay recorded games provides a way to study the behaviour of the problem. Selecting successful random games, truncating them and running multigame simulations using a successful base shows that the seeds of success are sown early. It was possible to better the results of good random game scores (mid 80s) pushing the new scores up into the mid 90s. Unfortunately this technique when applied to Chris Rosin's 177 move record game did not produce any new records :(. Surprisingly however, it was possible to produce games in the high 160's! using random play with a truncated (approx 60%) version of this game.
The ability to replay recorded games provides a way to study the behaviour of the problem. Selecting successful random games, truncating them and running multigame simulations using a successful base shows that the seeds of success are sown early. It was possible to better the results of good random game scores (mid 80s) pushing the new scores up into the mid 90s. Unfortunately this technique when applied to Chris Rosin's 177 move record game did not produce any new records :(. Surprisingly however, it was possible to produce games in the high 160's! using random play with a truncated (approx 60%) version of this game.
Line 17: Line 17:
Internally, the grid is automatically expanded as needed whenever a cell is played on an outer edge. When this happens only the affected side is expanded. As a side effect the grid numbering will seem unusual. The game log shows all row/col coordinates relative to the 1,1 origin located at the intersection of the lines containing top and left most edges of the cross. A fixed grid might be more efficient. With the ability to detect an off page condition and save and replay the game later this could be easier to deal with. The issue of grid origin comes up all the time when working with the code and debugging output.
Internally, the grid is automatically expanded as needed whenever a cell is played on an outer edge. When this happens only the affected side is expanded. As a side effect the grid numbering will seem unusual. The game log shows all row/col coordinates relative to the 1,1 origin located at the intersection of the lines containing top and left most edges of the cross. A fixed grid might be more efficient. With the ability to detect an off page condition and save and replay the game later this could be easier to deal with. The issue of grid origin comes up all the time when working with the code and debugging output.


The program produces a crude ASCII art grid; however and more importantly it produces a game log that can be replayed in the Pentasol free player. This can be used to capture graphical grids as well as providing independent validation of the games.
For an example of a loadable and re-playable game see [[Morpion_solitaire/Rosin177|Chris Rosin's 177 move 5T previous record holder]].

Most of the [http://www.morpionsolitaire.com/English/RecordsGrids5T.htm Record Games] have downloadable games in this notation. For example [http://www.morpionsolitaire.com/Grid5T178Rosin.txt Chris Rosin's 178 move grid].


For more see: Morpion -h|-help
For more see: Morpion -h|-help


===Basic Solution===
== Core Morpion Routines ==
The code need to solve the task requirements is found in this section. Just delete the $define below. The basic code will play a single random game, show an ASCII art grid, and produce a log in Pentasol compatible notation. On any given run your most likely to see a game with scores in the 20's or 60's.
<lang Unicon>link printf,strings,options
===Main and Core Game Play Procedures===
<syntaxhighlight lang="unicon">link printf,strings,options


$define MORPVER "1.7b" # version
$define MORPVER "1.7g" # version
$define EXTENDED 1 # delete line for basic


procedure main(A) # Morphion
procedure main(A) # Morphion
$ifdef EXTENDED
MorpionConf(A)
MorpionConf(A) # conf extended version
if \M_ReplayFile then ReplayMorpion()
if \M_ReplayFile then ReplayMorpion()
else if \M_Limit === 1 then PlayMorphion5T()
else if \M_Limit === 1 then ShowGame(SingleMorpion())
else MultiSimMorphion(\M_Limit)
else MultiMorphion(\M_Limit)
$else
printf("Finished.\n")
printf("--- Morpion Solitaire 5 (v%s) (single random game)---\n\n",MORPVER)
M_Strategy := RandomPlayer
M_Mvalid := ValidMove5T # can be changed to 5D
M_WriteGame := WriteMoveLogPS
M_Output := &output
ShowGame(SingleMorpion())
$endif
end
end


$define XEMPTY "." # symbols used in Grid
record morpioncell(symbol,direction,row,col) # a grid cell
$define XINIT "*"
record morpionmove(direction,move,line,roff,coff) # move & line
$define XUSED "+"
record morpiongame(grid, # the grid
$define DHOR "-" # Directions for moves
$define DVER "|"
$define DRD "\\"
$define DLD "/"
$define DALL "-|/\\"

global M_Strategy,M_Mvalid # Pluggable procedures
global M_Output # output files
global M_WriteGame # for logger
record morpiongame(grid,score, # the grid & score
log,history, # move log and replayable history log
log,history, # move log and replayable history log
roff,coff,center, # origin expansion offsets and center
roff,coff,center, # origin expansion offsets and center
Line 43: Line 67:
count, # game number (multi-game)
count, # game number (multi-game)
rseed) # &random at start of random play
rseed) # &random at start of random play
record morpionmove(direction,move,line,roff,coff) # move & line data
record morpioncell(symbol,direction,row,col) # a grid cell
record MorpionGameRecord(ref,row,col,darow,dacol) # record of game


procedure SingleMorpion(MG,N) #: Play a game silently
global M_Strategy,M_Eval,M_Mvalid # Pluggable procedures
/MG := SetupM5Grid() # start game with new grid?
global M_CommandLine,M_Config,M_PrintOpt,M_GameSave # Misc.
while MorphionMove(M_Strategy(MG)) do # keep moving
global M_Limit,M_StatUpd,M_BestL,M_WorstL # Multi-game simulation options
global M_SrchWid,M_SrchDep # For strategy modules
if MG.score >= \N then break # unless truncated
global M_ReplayFile,M_ReplayAfter # For game replay

$define XEMPTY "." # symbols used in Grid
$define XINIT "*"
$define XUSED "+"

$define DHOR "-" # Directions for moves
$define DVER "|"
$define DRD "\\"
$define DLD "/"

procedure PlayMorphion5T() #: Play a 5T game
G := (MG := SetupM5Grid()).grid # start new game
&random := MG.rseed := \M_Rseed # set &random
if M_PrintOpt = 1 then PrintGrid(MG)
pg := if M_PrintOpt == 2 then PrintGrid else 1
while MorphionMove(M_Strategy(pg(MG))) # keep moving
if M_PrintOpt = 1 then PrintGrid(MG)
if M_PrintOpt > 0 then {
PrintLog(MG)
WriteMoveLog(MG)
}
return MG
return MG
end
end


procedure MorphionMove(MG) #: make a move
procedure MorphionMove(MG) #: make a move
(M := MG.move).roff := MG.roff
(M := MG.move).roff := MG.roff # copy offsets
M.coff := MG.coff
M.coff := MG.coff
put(MG.history,M)
put(MG.history,M) # save history
log := FormatMoveLog(M)
MG.score +:= 1 # and score
\M_LogDetails(MG,M) # for analysis
log ||:= sprintf(" - of %i choices.",*MG.pool) # append # choices
log ||:= sprintf(" Metric=%i",M_Eval(MG)) # append score (opt)
put(MG.log,log) # log the move
every x := !M.line do { # draw the line
every x := !M.line do { # draw the line
g := MG.grid[x[1],x[2]]
g := MG.grid[x[1],x[2]]
Line 102: Line 105:
( c := 5 to *G[r := 1] ) do # down & left
( c := 5 to *G[r := 1] ) do # down & left
ScanGridLine(G,r,c,+1,-1,DLD,MG.pool)
ScanGridLine(G,r,c,+1,-1,DLD,MG.pool)
if MG.score = 0 & M_Strategy ~=== Replayer then { # move 1 special case

if *MG.history = 0 & M_Strategy ~=== Replayer then { # move 1 special case
every put(pool1 := [], MG.pool[2|19|20|26]) # cor. o(2), i(1), val o(1)
every put(pool1 := [], MG.pool[2|19|20|26]) # cor. o(2), i(1), val o(1)
MG.pool := pool1
MG.pool := pool1
Line 127: Line 129:
return pool
return pool
end
end

procedure ValidMove5T(L,dir) #: Succeed if L has valid 5T move
procedure ValidMove5T(L,dir) #: Succeed if L has valid 5T move
local i,j
local i,j
Line 135: Line 137:
every (j := 0) +:= ( find(dir,(!L).direction), 1)
every (j := 0) +:= ( find(dir,(!L).direction), 1)
if j > 1 then fail # no overlap, =1 implies at an end
if j > 1 then fail # no overlap, =1 implies at an end
return # that's it!
end

procedure ValidMove5D(L,dir) #: Succeed if L has valid 5D move #@@
local i,j
if *L ~= 5 then fail # wrong count
every (i := 0) +:= (/(!L).symbol,1)
if i ~= 1 then fail # more than 1 avail space
every (j := 0) +:= ( find(dir,(!L).direction), 1)
if j > 0 then fail # no overlap, =1 implies at an end
return # that's it!
return # that's it!
end
end
Line 148: Line 160:
G[r,c] := morpioncell(XINIT,"",r,c)
G[r,c] := morpioncell(XINIT,"",r,c)
}
}
return morpiongame(G,[],[],0,0,1 + (*G-1)/2.,,,,&random) # Create game
return morpiongame(G,0,[],[],0,0,1 + (*G-1)/2.) # Create game
end
end


Line 155: Line 167:
rn := *(G := MG.grid) # capture ...
rn := *(G := MG.grid) # capture ...
cn := *G[1] # ... entry dimensions
cn := *G[1] # ... entry dimensions
if \(!G)[1].symbol then { # left edge
if \(!G)[1].symbol then { # left edge
MG.coff +:= 1
MG.coff +:= 1
Line 177: Line 188:
}
}
return MG
return MG
end

procedure ShowGame(MG) #: show games
if M_Output === &output then
every (\(PrintGrid|WriteMoveLog|M_PrintDetails))(MG)
else # header first to output, game saved
every (\(WriteMoveLog|PrintGrid|M_PrintDetails))(MG)
end
end


procedure PrintGrid(MG) #: print the current Grid
procedure PrintGrid(MG) #: print the current Grid
G := MG.grid
G := MG.grid
every (ruler := " ") ||:= (1 to *G[1]) % 10
write("\nMorphion Solitare Grid (move=",*(L := MG.log),"):\n")
every (ruler := " ") ||:= (1 to *G[1]) % 10
fprintf(M_Output,"\nMorphion Solitare Grid (move=%i):\n%s\n",MG.score,ruler)
write(ruler)
every r := 1 to *G do {
every r := 1 to *G do {
writes(right(r%100,2)," ")
fprintf(M_Output,"%s ",right(r%100,2))
every c := 1 to *(G[r]) do
every c := 1 to *(G[r]) do
writes(\G[r,c].symbol | XEMPTY)
fprintf(M_Output,"%s",\G[r,c].symbol | XEMPTY)
write()
fprintf(M_Output,"\n")
}
}
write(ruler,"\n")
fprintf(M_Output,"%s\n",ruler)
return MG
return MG
end
end


procedure PrintLog(MG) #: print the log
procedure RandomPlayer(MG) #: Simulate random player
if &random := \M_Rseed then M_Rseed := &null # set seed if given only once
printf("Move Log\n")
/MG.rseed := &random # seed for this game
every printf("%i : %s\n",i := 1 to *MG.log,MG.log[i])
if MG.move := ?ScanGrid(MG).pool then return MG
end</syntaxhighlight>

===Game Logging===
<syntaxhighlight lang="unicon">procedure WriteMoveLog(MG) #: write move log wrapper
if \M_GameSave then {
savegame := sprintf("Games/%s/%i-L%i",M_GameType,*MG.history,M_Limit)
if M_Limit ~= 1 then
savegame ||:= sprintf("(%i)",MG.count)
if \M_ReplayFile then {
fn := map(M_ReplayFile,"/\\","__")
fn ? (="Games/", savegame ||:= "-RF" || tab(find(".txt")))
savegame ||:= "-RN" || (0 < \M_ReplayAfter)
}
savegame ||:= sprintf("_%s-%s-F%s.txt",deletec(&date,'/'),deletec(&clock,':'),M_GameFmt)
M_GameSave := savegame
fprintf(M_Output,WriteMoveLogHeader(MG)) # write header, game is saved
f := open(savegame,"w") | stop("Unable to open ",savegame," for writing")
fprintf(f,M_WriteGame(MG),M_Config) # call desired writer for output/save
close(f)
}
else
fprintf(M_Output,M_WriteGame(MG))
end
end


procedure FormatMoveLog(M,roff,coff) #: format a log entry
procedure WriteMoveLogHeader(MG) #: write common header comments
return sprintf("#\n# Game Record for Morphion %s game of %i moves\n_
/M.roff := \roff | 0
# Date: %s\n# Saved: %s\n# &random: %i\n_
/M.coff := \coff | 0
# ReplayFile: %s (%s moves)\n#\n",
log := sprintf("\"%s\" [%i,%i] : ",M.direction,
M.line[M.move,1]-M.roff,M.line[M.move,2]-M.coff)
\M_GameType|"5T",MG.score,&date,\M_GameSave|"* none *",
every x := !M.line do
MG.rseed,\M_ReplayFile|"* none *",\M_ReplayAfter|"* all *")
log ||:= sprintf("[%i,%i] ",x[1]-M.roff,x[2]-M.coff)
return log
end
end


procedure RandomPlayer(MG) #: Simulate random player
procedure WriteMoveLogPS(MG) #: write pentasol style move log
l := WriteMoveLogHeader(MG)
if MG.move := ?ScanGrid(MG).pool then return MG
l ||:= sprintf("# Pentasol compatible format\n#\n#\n_
# Morpion Solitaire game\n#\n_
# XXXX\n# X X\n# X X\n_
# XXXR XXXX\n# X X\n# X X\n_
# XXXX XXXX\n# X X\n# X X\n# XXXX\n#\n_
# R = reference point\n_
# List of moves starts with reference point (col,row)\n_
# Lines are\n# (col,row) <direction> <+/-centerdist>\n_
# distance to center is left side or top edge\n#\n")
l ||:= sprintf("(%i,%i)\n",4+MG.coff,4+MG.roff)
every l ||:= FormatMoveLogPS(MG,!MG.history)
return l || "#"
end
end


procedure Scorefail(MG);end #: dummy always fails</lang>
procedure FormatMoveLogPS(MG,m) #: format a PS move
d := if m.direction == "/" then m.move-3 else 3-m.move
return sprintf("(%i,%i) %s %s%d\n",
m.line[m.move,2]-m.coff+MG.coff,m.line[m.move,1]-m.roff+MG.roff,
m.direction,(d < 0,"-")|(d = 0,"")|"+",abs(d))
end</syntaxhighlight>


===Extended Framework===
== Strategy Support ==
None of the code below is needed to satisfy the basic task. It supports the extended framework which includes, command line options, reading and replaying games from a saved file, running mass simulations to glean the best games, and some code for detailed analysis if I ever get around to experimenting with other than random strategy.
<lang Unicon># No useful examples at this time</lang>
===Interface, Parameters, Globals===


<syntaxhighlight lang="unicon">$ifdef EXTENDED
== Interface & Parameters ==

<lang Unicon>procedure Usage()
# --- Interface, Parameters, Additional Globals ---
fprintf(&errout,"_

Morphion [options] Plays the 5T variant of Morphion Solitaire\n_
global M_Eval # Pluggable procedure
Arguments : ")
global M_SrchWid,M_SrchDep # For strategy modules
every fprintf(&errout," %s",!M_CommandLine)
global M_LogDetails,M_PrintDetails # Misc.
fprintf(&errout,"_
global M_CommandLine,M_Config,M_GameType,M_GameSave
Morphion [options] Plays the 5T variant of Morphion Solitaire\n_
global M_Limit,M_StatUpd,M_BestL,M_WorstL # Multi-game simulation options
Where options are:\n_
global M_ReplayFile,M_ReplayAfter,M_Rseed # For game replay
\t-P|-print\tcontrols how much is printed \n_
global M_ReadGame,M_GameFmt # Game formats to use
\t\t\tn=2\tshows all boards and a log of all moves (every game)\n_
global M_ChartW,M_ChartG # histogram
\t\t\tn=1\tshows a log of the moves and final board (default single game)\n_

\t\t\tn=0\tproduces no per game output (default for multigames)\n_
# --- Beginning of Non-core (Extended) code ---
\t-A\tchoose player strategy approach (default=random, future)\n_
\t-E\tSpecifiy an position fitness evaluation function (default none, future)\n_
\t-SW\tSearch width (strategy dependent, future)\n_
\t-SD\tSearch depth (strategy dependent, future)\n_
\t-R|-replay\treplay\n_
\t-RF\tfile containing game record to be replayed\n_
\t-RN\tnumber of moves to replay (0=all)\n_
\t-seed\tstart the seed of the random number at this value\n_
\t-L|-limit\tgames to play (if 0 or less then play until any of 'XxQq' is pressed\n_
\t\tnote for larger n this benefits from larger BLKSIZE, STRSIZE environment variables\n_
\t-UN\tGive status update notifications every n simulations\n_
\t-B\tKeep best n games of unique length (default 5)\n_
\t-W\tKeep worst n games of unique length (default 3)\n_
\t-?|-h|-help\tthis help text")
stop()
end


procedure MorpionConf(A) # Configure the Solver
procedure MorpionConf(A) # Configure the Solver
M_CommandLine := copy(A) # preserve
M_CommandLine := copy(A) # preserve
os := "-Q! -L+ -limit+ -P+ -print+ -R! -replay! -RF: -RN+ -seed+ -save! "
os := "-Q! -L+ -limit+ -V: -variant: -seed+ "
os ||:= "-R! -replay! -RF: -RN+ -save! -RW: -RR: "
os ||:= "-histwidth+ -histgroup+ -HW+ -HG+ "
os ||:= "-UN+ -SW+ -SD+ -A: -E+ -B+ -W+"
os ||:= "-UN+ -SW+ -SD+ -A: -E+ -B+ -W+"
os ||:= "-details! "
opt := options(A,os,Usage) # -<anything else> gets help
opt := options(A,os,Usage) # -<anything else> gets help
M_Limit := ( 0 <= integer(\opt["limit"|"L"])) | 1
M_Limit := ( 0 <= integer(\opt["limit"|"L"])) | 1
M_GameSave := opt["save"]
M_Rseed := \opt["seed"]
M_PrintOpt := (0|1|2) = \opt["P"|"print"]
M_Mvalid := case opt["V"|"variant"] of {
"5D" : (M_GameType := "5D", ValidMove5D)
/M_PrintOpt := \M_Limit === 1 | 0
default : (M_GameType := "5T", ValidMove5T) # also 5T
if \opt["R"|"replay"] then {
M_ReplayFile := \opt["RF"] | "Games/177-5T-rosin.txt"
}
M_ReplayAfter := (0 < \opt["RN"]) | &null
M_ReadGame := case map(\opt["RR"]) | &null of {
default : (M_GameFmt := "ps", ReadMoveLogPS)
}
"0" : (M_GameFmt := "0", ReadMoveLogOrig) # deprecated
else M_ReplayFile := &null
M_Rseed := \opt["seed"]
}
M_WriteGame := case map(\opt["RW"]) | &null of {
default : (M_GameFmt := "ps", WriteMoveLogPS)
"0" : (M_GameFmt := "0", WriteMoveLogOrig) # deprecated
}
M_Strategy := case opt["A"] of {
M_Strategy := case opt["A"] of {
"A1" : (def_un := 50, PlayerA1)
"A1" : (def_un := 50, PlayerA1)
default : (def_un := 500, RandomPlayer)
default : (def_un := 500, RandomPlayer)
}
}
M_Eval := case opt["E"] of {
"1" : Score1 # test
default : &null # also "0"
}
M_ChartW := (40 <= \opt["histwidth"|"HW"]) | 80
M_ChartG := \opt["histgroup"|"HG"] | 5
M_LogDetails := if \opt["details"] then LogDetails else 1
M_PrintDetails := if \opt["details"] then PrintDetails else 1
M_StatUpd := (0 < \opt["UN"]) | def_un
M_StatUpd := (0 < \opt["UN"]) | def_un
M_BestL := (0 < \opt["B"]) | 5
M_BestL := (0 < \opt["B"]) | 5
M_WorstL := (0 < \opt["W"]) | 0
M_WorstL := (0 < \opt["W"]) | 0
M_SrchWid := (0 < \opt["SW"]) | 5
M_SrchWid := (0 < \opt["SW"]) | 5
M_SrchDep := (0 < \opt["SD"]) | 5
M_SrchDep := (0 < \opt["SD"]) | 5
M_Eval := case opt["E"] of {
if \opt["R"|"replay"] then {
"1" : Score1 # test
M_ReplayFile := \opt["RF"] | "Games/5T/177-5T-rosin.txt"
default : &null # also "0"
M_ReplayAfter := (0 < \opt["RN"]) | &null
}
}
else M_ReplayFile := &null
if \(M_GameSave := opt["save"]) then {
fn := sprintf("Runs/%s-L%i-",M_GameType,M_Limit)
fn ||:= sprintf("RF%s-",map(\M_ReplayFile,"/\\","__"))
fn ||:= sprintf("RN%i-",\M_ReplayAfter)
fn ||:= sprintf("%s-%s.txt",deletec(&date,'/'),deletec(&clock,':'))
M_Output := open(fn,"w")
}
/M_Output := &output
c := sprintf("# --- Morpion Solitaire 5 (v%s) ---\n#\n",MORPVER)
c := sprintf("# --- Morpion Solitaire 5 (v%s) ---\n#\n",MORPVER)
c ||:= "# Command line options :"
c ||:= "# Command line options :"
every c ||:= " " || !A
every c ||:= " " || !A
c ||:= "\n# Summary of Morpion Configuration:\n"
c ||:= "\n# Summary of Morpion Configuration:\n"
c ||:= sprintf("# Variant (5T/5D) move validation = %i\n",M_Mvalid := ValidMove5T)
c ||:= sprintf("# Variant (5T/D) move validation = %i\n",M_Mvalid)
c ||:= sprintf("# Games to play = %s\n",(0 < M_Limit) | "* unlimited *")
c ||:= sprintf("# Games to play = %s\n",(
0 < M_Limit) | "* unlimited *")
c ||:= sprintf("# Print (0=none,1=minimal,2=all) = %i\n",M_PrintOpt)
c ||:= sprintf("# Games will be saved = %s\n",\M_SaveGame)
c ||:= "# Multi-game options:\n"
c ||:= "# Multi-game options:\n"
c ||:= sprintf("# - Status Updates = %i\n",M_StatUpd)
c ||:= sprintf("# - Status Updates = %i\n",M_StatUpd)
c ||:= sprintf("# - Keep best = %i\n",M_BestL)
c ||:= sprintf("# - Keep best = %i\n",M_BestL)
c ||:= sprintf("# - Keep worst = %i\n",M_WorstL)
c ||:= sprintf("# - Keep worst = %i\n",M_WorstL)
c ||:= sprintf("# - Histogram width = %i\n",M_ChartW)
c ||:= sprintf("# - Histogram grouping = %i\n",M_ChartG)
c ||:= sprintf("# Games will be saved = %s\n",
(\M_GameSave, "Yes") | "* No *")
c ||:= sprintf("# - Format for game file (write) = %i\n",\M_WriteGame)
c ||:= "# Replaying\n"
c ||:= "# Replaying\n"
c ||:= sprintf("# - Load recorded game file = %i\n",\M_ReplayFile | "* None *")
c ||:= sprintf("# - Format for game file (read) = %i\n",\M_ReadGame)
c ||:= sprintf("# - Moves to replay = %i\n",0 ~= \M_ReplayAfter)
c ||:= sprintf("# - Game file to be replayed = %s\n",
\M_ReplayFile | "* None *")
c ||:= sprintf("# - Moves to replay = %i\n",
0 ~= \M_ReplayAfter)
c ||:= sprintf("# Player Strategy = %i\n",M_Strategy)
c ||:= sprintf("# Player Strategy = %i\n",M_Strategy)
c ||:= sprintf("# - Position Fitness Evaluator = %i\n",\M_Eval | "* None *")
c ||:= sprintf("# - Seed for &random = %i\n",\M_Rseed)
c ||:= sprintf("# - Position Fitness Evaluator = %s\n",
image(\M_Eval) | "* None *")
c ||:= sprintf("# - Search Width (strategy dependant) = %i\n",M_SrchWid)
c ||:= sprintf("# - Search Width (strategy dependant) = %i\n",M_SrchWid)
c ||:= sprintf("# - Search Depth (strategy dependant) = %i\n",M_SrchDep)
c ||:= sprintf("# - Search Depth (strategy dependant) = %i\n",M_SrchDep)
c ||:= "#\n# Running:\n"
c ||:= sprintf("# Log Details for analysis = %s\n",
if M_LogDetails === 1 then "No" else "Yes")
c ||:= "#\n"
M_Config := c
M_Config := c
if \opt["Q"] then stop(M_Config,"-Q Stops run after processing options")
if \opt["Q"] then stop(M_Config,"-Q Stops run after processing options")
else write(M_Config)
else fprintf(M_Output,M_Config)
/M_Eval := Scorefail
/M_Eval := Scorefail
end</lang>
end

== Multigame Simulation and Monitoring Support ==
procedure Usage()
<lang Unicon>procedure MultiSimMorphion(N) #: Simulate N games
fprintf(&errout,"_
Morphion [options] Plays the 5T/D variant of Morphion Solitaire\n_
Arguments : ")
every fprintf(&errout," %s",!M_CommandLine)
fprintf(&errout,"_
Morphion [options] Plays the 5T/D variant of Morphion Solitaire\n_
Where options are:\n_
\t-A\tchoose player strategy approach (default=random, future)\n_
\t-E\tSpecifiy an position fitness evaluation function (default none, future)\n_
\t-SW\tSearch width (strategy dependent, future)\n_
\t-SD\tSearch depth (strategy dependent, future)\n_
\t-V|-variant\t5D or 5T (default)\n_
\t-R|-replay\treplay\n_
\t-RF\tfile containing game record to be replayed\n_
\t-RN\tnumber of moves to replay (0=all)\n_
\t-RR\tgame recording format to read (ps=pentasol(default), 0=original)\n_
\t-RW\tgame recording format to write ps=pentasol(default), 0=original)\n_
\t-save\tsave the best (-B) and worst (-W) games\n_
\t-seed\tstart the seed of the random number at this value\n_
\t-L|-limit\tgames to play (if 0 or less then play until any of 'XxQq' is pressed\n_
\t\tnote for larger n this benefits from larger BLKSIZE, STRSIZE environment variables\n_
\t-HW|-histwidth\twidth (cols) of histogram of scores\n_
\t-HG|-histgroup\tsize of groups (buckets) for histogram\n_
\t-UN\tGive status update notifications every n simulations\n_
\t-B\tKeep best n games of unique length (default 5)\n_
\t-W\tKeep worst n games of unique length (default 3)\n_
\t-details\tLog game details for analysis\n_
\t-Q\t(debugging) terminates after options processing\n_
\t-?|-h|-help\tthis help text")
stop()
end</syntaxhighlight>

===Multigame Simulation and Monitoring Support===
<syntaxhighlight lang="unicon">
procedure MultiMorphion(N,MG) #: Simulate N games using MG
etime := -&time
etime := -&time
scores := table(n := 0)
scores := table(n := 0)
every bestL|worstL := []
every bestL|worstL := []
if N <= 0 then N := "unlimited"
if N <= 0 then N := "unlimited"
repeat {
repeat {
if n >= numeric(N) then break else n +:= 1
if n >= numeric(N) then break else n +:= 1
scores[*(mg := PlayMorphion5T()).log] +:= 1
mg := SingleMorpion(deepcopy(\MG)|&null) # play out game
mg.count := n # game number
scores[mg.score] +:= 1 # count score
if short := ( /short | *(\short).log >= *mg.log, mg) then {
mg.count := n # game number
push(worstL,short) # keep worst
if short := ( /short | short.score >= mg.score, mg) then {
push(worstL,short) # keep worst
if *worstL > M_WorstL then pull(worstL)
if *worstL > M_WorstL then pull(worstL)
}
}
if long := ( /long | *(\long).log <= *mg.log, mg) then {
if ( /long | long.score <= mg.score) then {
put(bestL,long) # keep best
bestcnt := if (\long).score = mg.score then bestcnt + 1 else 1
long := mg
put(bestL,long) # keep best
if *bestL > M_BestL then get(bestL)
if *bestL > M_BestL then get(bestL)
printf("Longest game %i after %i simulations.\n",*long.log,n)
fprintf(M_Output,"Longest game %i after %i simulations &random=%i.\n",
long.score,n,long.rseed)
fprintf(&errout,"\r%i of %s simulations, long=%i (%s)",
n,N,*long.log,&dateline)
fprintf(&errout,"\r%i of %s simulations, long=%i(%i) (%s %s)",
n,N,long.score,bestcnt,&date,&clock)
}
}
if (n % M_StatUpd) = 0 then # show we're alive and working
if (n % M_StatUpd) = 0 then # say we're alive & working
fprintf(&errout,"\r%i of %s simulations, long=%i (%s)",
fprintf(&errout,"\r%i of %s simulations, long=%i(%i) (%s %s)",
n,N,*long.log,&dateline)
n,N,long.score,bestcnt,&date,&clock)
if kbhit() & getch() == !"QqXx" then # exit if any q/x
if kbhit() & getch() == !"QqXx" then # exit if any q/x
break fprintf(&errout,"\nExiting after %i simulations.\n",n)
break fprintf(&errout,"\nExiting after %i simulations.\n",n)
}
}
Line 336: Line 451:
avg +:= i * scores[i]
avg +:= i * scores[i]
}
}
printf("\nResults from Sample of %i games of Morpion 5T:\n",n)
fprintf(M_Output,"\nResults from Sample of %i games of Morpion 5T/D:\n",n)
printf("Shortest game was %i moves.\n",short)
fprintf(M_Output,"Shortest game was %i moves.\n",short)
printf("Average game was %i moves.\n",avg /:= n)
fprintf(M_Output,"Average game was %i moves.\n",avg /:= n)
printf("Longest game was %i moves.\n",*long.log)
fprintf(M_Output,"Longest game was %i moves.\n",long.score)
printf("Average time/game is %i ms.\n",etime/real(n))
fprintf(M_Output,"Average time/game is %i ms.\n",etime/real(n))
fprintf(M_Output,"&random is now %i.\n",&random)
GraphScores(scores) # graph results
GraphScores(scores) # graph results
printf("\nLongest (%i) Game(s):\n",M_BestL)
fprintf(M_Output,"\nLongest (%i) Game(s):\n",M_BestL)
every x := !reverse(bestL) do { # show longest game(s) and log
every ShowGame(!reverse(bestL)) # show longest game(s) and log
fprintf(M_Output,"\nShortest (%i) Game(s):\n",M_WorstL)
PrintGrid(x)
WriteMoveLog(x)
every ShowGame(!worstL) # show longest game(s) and log
PrintLog(x)
}
printf("\nShortest (%i) Game(s):\n",M_WorstL)
every x := !worstL do { # show longest game(s) and log
PrintGrid(x)
PrintLog(x)
WriteMoveLog(x)
}
MemUsage() # diagnostic
MemUsage() # diagnostic
end
end

$define CHARTW 80 # chart width
$define CHARTG 20 # size of chart buckets


procedure GraphScores(S) #: graph results
procedure GraphScores(S) #: graph results
chart := []
chart := []
every s := key(S) do { # by score
every s := key(S) do { # by score
n := s/CHARTG+1 # chunks of ...
n := s/M_ChartG+1 # chunks of ...
until chart[n] do put(chart,0) # grow chart to need
until chart[n] do put(chart,0) # grow chart to need
chart[n] +:= S[s]
chart[n] +:= S[s]
}
}
s := (1 < max!chart/CHARTW | 1) # scale
s := (1 < max!chart/M_ChartW | 1) # scale
printf("\nSummary of Results every '*' = %d games\n",s)
fprintf(M_Output,"\nSummary of Results every '*' = %d games\n",s)
every n := 1 to *chart do
every n := 1 to *chart do
printf("%3d | (%6d) %s\n",(n-1)*CHARTG,chart[n],repl("*",chart[n]/s))
fprintf(M_Output,"%3d | (%6d) %s\n",(n-1)*M_ChartG,chart[n],repl("*",chart[n]/s))
end
end


procedure MemUsage() #: monitor usage
procedure MemUsage() #: monitor usage
printf("\nTotal run time = %i ms\n",&time)
fprintf(M_Output,"\nTotal run time = %i ms\n",&time)
printf("&allocated (Total,static,string,block) : ")
fprintf(M_Output,"&allocated (Total,static,string,block) : ")
every printf(" %i",&allocated) ; printf("\n")
every fprintf(M_Output," %i",&allocated) ; fprintf(M_Output,"\n")
printf("&collections (Total,static,string,block) : ")
fprintf(M_Output,"&collections (Total,static,string,block) : ")
every printf(" %i",&collections) ; printf("\n")
every fprintf(M_Output," %i",&collections) ; fprintf(M_Output,"\n")
printf("&regions ( - , - ,string,block) : ")
fprintf(M_Output,"&regions ( - , - ,string,block) : ")
every printf(" %s","-"|&regions) ; printf("\n")
every fprintf(M_Output," %s","-"|&regions) ; fprintf(M_Output,"\n")
printf("&storage ( - , - ,string,block) : ")
fprintf(M_Output,"&storage ( - , - ,string,block) : ")
every printf(" %s","-"|&storage) ; printf("\n\n")
every fprintf(M_Output," %s","-"|&storage) ; fprintf(M_Output,"\n\n")
fprintf(M_Output,"Icon/Unicon version %s\n",&version)
end</lang>
end </syntaxhighlight>
== Game Reloader/Replayer ==

<lang Unicon>procedure ReplayMorpion() #: Handle recorded games
===Game Replayer===
<syntaxhighlight lang="unicon">procedure ReplayMorpion() #: Handle recorded games
Replayer(M := ReadMoveLog(M_ReplayFile)) # read game and save data
Replayer(M := ReadMoveLog(M_ReplayFile)) # read game and save data
M_Strategy := Replayer
if /M_ReplayAfter | (M_ReplayAfter > *M) then {
if /M_ReplayAfter | (M_ReplayAfter > *M) then {
printf("Single game replay\n")
fprintf(M_Output,"Single game replay\n")
ShowGame(SingleMorpion())
M_Strategy := Replayer
PlayMorphion5T()
}
}
else { # truncation replay
else { # truncation replay
G := (MG := SetupM5Grid()).grid # start new grid
MG := SingleMorpion(,M_ReplayAfter) # play shortened game
M_Strategy := RandomPlayer
while MorphionMove(Replayer(MG)) do
if *MG.history = M_ReplayAfter then # play until move
break
(SetupM5Grid := ReSetupM5Grid)(MG) # replace procedure and set grid
&random := MG.rseed := \M_Rseed # set &random per M_Seed
if M_Limit === 1 then
if M_Limit === 1 then
PlayMorphion5T() # single game
ShowGame(SingleMorpion(MG)) # single game
else
else
MultiSimMorphion(M_Limit) # simulate many games from here
MultiMorphion(M_Limit,MG) # simulate many games from here
}
}
return
return
end
end


procedure ReSetupM5Grid(MG) #: Used to override initial setup
procedure Replayer(MG) #: feed replayed moves from list/game
static MGB
if MGB := \MG then return # 'set' partial game if arg
else return deepcopy(MGB) # otherwise return a new copy
end

procedure Replayer(MG) #: feed replayed moves
static ML,radj,cadj
static ML,radj,cadj
if type(MG[1]) == "MorpionGameRecord" then return ML := MG
if type(MG[1]) == "MorpionGameRecord" then return ML := MG # setup list
if not ScanGrid(MG) then fail
if not ScanGrid(MG) then fail # out of moves ?
x := get(ML) | fail
x := get(ML) | fail # get next move
if x.ref = 0 then x := get(ML) | fail
if x.ref = 0 then x := get(ML) | fail # skip move 0 if any
xr := x.row + MG.roff # adjust for expansion
xr := x.row + MG.roff # adjust move for grid expansion
xc := x.col + MG.coff
xc := x.col + MG.coff
dr := \x.darow + MG.roff # adjust end for grid expansion
dc := \x.dacol + MG.coff
pool := []
pool := []
every m := !MG.pool do {
every m := !MG.pool do { # find possible moves here
mr := m.line[m.move,1]
mr := m.line[m.move,1]
mc := m.line[m.move,2]
mc := m.line[m.move,2]
if xr=mr & xc=mc then
if xr=mr & xc=mc then
if \dr & \dc then { # info to disambiguate?
put(pool,m)
}
every p := (m.line)[1|5] do # try endpoints
if MG.move := pool[*pool = 1] then # is move unique ?
if p[1] = dr & p[2] = dc then
put(pool,m) # save matching move
return MG
}
else # no - need to disambiguate
if /(x.darow|x.dacol) then {
else
printf("Ambiguous position at move #%i with %i choices\n_
put(pool,m) # save matching move(s)
}
Need unique disambiguation, syntax: n:r,c^r,c\n",*MG.log+1,*pool)
if *pool = 1 then # unique move?
every m := !pool do
printf(" %s\n",FormatMoveLog(m,MG.roff,MG.coff))
return ( MG.move := pool[1], MG) # set unique move and return MG
else { # we have a problem
}
else {
ShowGame(MG)
fprintf(M_Output,"Problem encountered replaying game at move #%i, %i choices.\n",MG.score,*pool)
dr := x.darow + MG.roff # adjust for expansion
dc := x.dacol + MG.coff
every m := !pool do
fprintf(M_Output," %s\n",FormatMoveLogPS(MG,m))
every p := ((m := !pool).line)[1|5] do
if p[1] = dr & p[2] = dc then {
&dump := 0
MG.move := m
stop()
return MG
}
end</syntaxhighlight>
}
printf("No Match found to disambiguate move #%i\n",*MG.log+1)
every m := !pool do
printf(" %s\n",FormatMoveLog(m,MG.roff,MG.coff))
}
PrintGrid(MG)
PrintLog(MG)
WriteMoveLog(MG)
stop()
end


===Game Reader===
record MorpionGameRecord(ref,row,col,darow,dacol)
<syntaxhighlight lang="unicon">procedure ReadMoveLog(MG) #: read move log wrapper
fprintf(M_Output,"Reading recorded game from: %s\n",M_ReplayFile)
f := open(M_ReplayFile ,"r") |
stop("Unable to open file ",M_ReplayFile ," for read.")
R := []
while b := trim(read(f)) do {
if b ? ="$end" then break # allow pre-mature end of file
b ?:= tab(find("#")|0) # strip comments
b := deletec(b,' \t') # strip whitespace
if *b > 0 then put(R,b) # save move for reader
}
close(f)
return M_ReadGame(R) # call reader, return move list
end


procedure ReadMoveLog(fn) #: Read a move log
procedure ReadMoveLogPS(R) #: read pentasol style move log
static off
printf("Reading recorded game from: %s\n",fn)
initial { # precalc center offsets
f := open(fn,"r") | stop("Unable to open file ",fn," for read.")
off := table()
off[-2] := -4
off[-1] := -3
off[0] := 2
off[1] := -1
off[2] := 4
}
M := []
M := []
while b := trim(read(f)) do {
n := 0 # move number
get(R) ? ( ="(", coff := integer(tab(many(&digits))) - 4,
if b ? ="$end" then break # allow pre-mature end of file
=",", roff := integer(tab(many(&digits))) - 4,
b := deletec(b,' \t')
b ?:= tab(find("#")|0)
=")", pos(0) ) | # Reference Cell (c,r)
stop(&output,"Syntax error in reference line.")
until *b = 0 do {
while b := get(R) do { # Line (c,r) d o
x := MorpionGameRecord()
b ?:= ( x.ref := integer(tab(many(&digits))),=":", # base
b ? ( ="(", c := integer(tab(many(&digits))),
x.row := integer(=!["-","+",""]||tab(many(&digits))),=",",
=",", r := integer(tab(many(&digits))),
x.col := integer(=!["-","+",""]||tab(many(&digits))),
=")", d := tab(any(DALL)),
tab(0)) |
o := integer(=("-2"|"-1"|0|"+1"|"+2")) ) |
stop(&output,"Syntax error in line above.")
stop(&output,"Syntax error in line above.")
if b ?:= ( ="^", tab(0)) then { # disamb
x := MorpionGameRecord() # new move / line
x.ref := n +:= 1
b ?:= ( x.darow := integer(=!["-","+",""]||tab(many(&digits))),=",",
x.darow := x.row := r - roff
x.dacol := integer(=!["-","+",""]||tab(many(&digits))),
tab(0)) |
x.dacol := x.col := c - coff
case d of { # adjust based on direction
stop(&output,"Syntax error in line above (disamiguation).")
}
DHOR : x.dacol +:= off[o]
DVER : x.darow +:= off[o]
b ?:= ( =(";"|""), tab(0) )
put(M,x)
DRD : ( x.darow +:= off[o], x.dacol +:= off[o])
DLD : ( x.darow -:= off[o], x.dacol +:= off[o])
}
}
put(M,x)
}
}
return sortf(M,1)
return M
end</syntaxhighlight>

===Detailed Move Logging (for analysis)===
<syntaxhighlight lang="unicon">procedure PrintDetails(MG) #: print the log
fprintf(M_Output,"Detailed Move Log\n")
every fprintf(M_Output,"%i : %s\n",i := 1 to *MG.log,MG.log[i])
end
end


procedure WriteMoveLog(MG) #: write out a re-readable move log
procedure LogFormatMove(M,roff,coff) #: format a log entry
/M.roff := \roff | 0
savegame := sprintf("Games/%i-L%i",*MG.history,M_Limit)
if M_Limit ~= 1 then
/M.coff := \coff | 0
savegame ||:= sprintf("(%i)",MG.count)
log := sprintf("\"%s\" [%i,%i] : ",M.direction,
M.line[M.move,1]-M.roff,M.line[M.move,2]-M.coff)
if \M_ReplayFile then {
every x := !M.line do
M_ReplayFile ? (="Games/", savegame ||:= "-RF" || tab(find(".txt")))
savegame ||:= "-RN" || (0 < \M_ReplayAfter)
log ||:= sprintf("[%i,%i] ",x[1]-M.roff,x[2]-M.coff)
}
return log
end
savegame ||:= sprintf("_%s-%s.txt",deletec(&date,'/'),deletec(&clock,':'))

c := sprintf( "#\n# Game Record for Morphion 5T game of %i moves\n_
# Reference: %s\n_
procedure LogDetails(MG,M) #: Record details
# &random: %i\n_
log := LogFormatMove(M)
log ||:= sprintf(" - of %i choices.",*MG.pool) # append # choices
# Syntax for recorded games:\n_
log ||:= sprintf(" Metric=%i",M_Eval(MG)) # append score (opt)
# 1. whitespace and comments (everything past #) are ignored\n_
# 2. moves may be ; or newline separated\n_
put(MG.log,log) # log the move
end</syntaxhighlight>
# 3. moves are formatted (using BNF style variables like <x>):\n_

# <movenumber>:<row>,<col>[^<drow>,<dcol>]\n_
===Strategy Support===
# The optional drow/dcol values provide an additional point\n_
<syntaxhighlight lang="unicon"># No useful examples at this time
# used to disambiguate moves. The point drow/dcol must be\n_

# chosen at one end of the line.\n_
# Moves are applied in sorted order.\n_
procedure Scorefail(MG);end #: dummy M_Eval always fails
$endif</syntaxhighlight>
# 4. all row/col coordinates are relative to the 1,1 origin\n_
# located at the intersection of the lines containing the\n_
# top and left most edges of the cross.\n#\n",
*MG.history,savegame,MG.rseed)
every m := MG.history[i := 1 to *MG.history] do {
e := m.move ~= (1|5)
/l := ""
l ||:= sprintf("%i:%i,%i^%i,%i;",
i,m.line[m.move,1]-m.roff,m.line[m.move,2]-m.coff,
m.line[e,1]-m.roff,m.line[e,2]-m.coff)
if *l > 70 then {
c ||:= l || "\n"
l := &null
}
}
c ||:= \l || "\n"
write(c)
if \M_GameSave then {
f := open(savegame,"w") | stop("Unable to open ",savegame," for writing")
write(f,M_Config,c)
close(f)
}
end</lang>


{{libheader|Icon Programming Library}}
{{libheader|Icon Programming Library}}
Line 540: Line 636:


=== Help ===
=== Help ===
<pre>Morphion [options] Plays the 5T variant of Morphion Solitaire
<pre>Morphion [options] Plays the 5T/D variant of Morphion Solitaire
Arguments : -hMorphion [options] Plays the 5T variant of Morphion Solitaire
Arguments : -helpMorphion [options] Plays the 5T/D variant of Morphion Solitaire
Where options are:
Where options are:
-P|-print controls how much is printed
n=2 shows all boards and a log of all moves (every game)
n=1 shows a log of the moves and final board (default single game)
n=0 produces no per game output (default for multigames)
-A choose player strategy approach (default=random, future)
-A choose player strategy approach (default=random, future)
-E Specifiy an position fitness evaluation function (default none, future)
-E Specifiy an position fitness evaluation function (default none, future)
-SW Search width (strategy dependent, future)
-SW Search width (strategy dependent, future)
-SD Search depth (strategy dependent, future)
-SD Search depth (strategy dependent, future)
-V|-variant 5D or 5T (default)
-R|-replay replay
-R|-replay replay
-RF file containing game record to be replayed
-RF file containing game record to be replayed
-RN number of moves to replay (0=all)
-RN number of moves to replay (0=all)
-RR game recording format to read (ps=pentasol(default), 0=original)
-RW game recording format to write ps=pentasol(default), 0=original)
-save save the best (-B) and worst (-W) games
-seed start the seed of the random number at this value
-seed start the seed of the random number at this value
-L|-limit games to play (if 0 or less then play until any of 'XxQq' is pressed
-L|-limit games to play (if 0 or less then play until any of 'XxQq' is pressed
note for larger n this benefits from larger BLKSIZE, STRSIZE environment variables
note for larger n this benefits from larger BLKSIZE, STRSIZE environment variables
-HW|-histwidth width (cols) of histogram of scores
-HG|-histgroup size of groups (buckets) for histogram
-UN Give status update notifications every n simulations
-UN Give status update notifications every n simulations
-B Keep best n games of unique length (default 5)
-B Keep best n games of unique length (default 5)
-W Keep worst n games of unique length (default 3)
-W Keep worst n games of unique length (default 3)
-details Log game details for analysis
-?|-h|-help this help text
-Q (debugging) terminates after options processing
</pre>
-?|-h|-help this help text</pre>


=== Multigame ===


=== Random Game Record ===
The multigame simulation of completely random play includes a histogram showing game scores clustering in the 20's and 60's. This result is similar to results obtained by [http://www.morpionsolitaire.com/English/RecordsGrids5T.htm Jean-Jacques Sibil] (scroll down to find reference) who has run nearly a billion such simulations achieving a high score of 102 as of 2010.
The following image was obtained by replaying the game in Pentasol and saving the picture. The original was generated by a multigame simulation and represents the best game generated by this game from the starting position.


<pre># --- Morpion Solitaire 5 (v1.7b) ---
[[File:Morpion 5T92 unicon.PNG]]


This the game record used to produce the above image:
<pre>#
# Game Record for Morphion 5T game of 92 moves
# Date: 2012/02/18
# Saved: Games/5T/92-L1_20120218-163208-Fps.txt
# &random: 1617565851
# ReplayFile: * none * (* all * moves)
#
# Pentasol compatible format
#
#
# Morpion Solitaire game
#
# XXXX
# X X
# X X
# XXXR XXXX
# X X
# X X
# XXXX XXXX
# X X
# X X
# XXXX
#
# R = reference point
# List of moves starts with reference point (col,row)
# Lines are
# (col,row) <direction> <+/-centerdist>
# distance to center is left side or top edge
#
(9,7)
(12,8) | -2
(16,7) - -2
(9,8) | -2
(13,11) / 0
(9,9) | +2
(15,11) | -2
(13,13) - -2
(6,11) | -2
(16,10) - -2
(12,14) | -2
(8,4) - +2
(5,7) - +2
(13,6) \ 0
(10,12) \ 0
(14,8) \ 0
(14,12) / 0
(10,10) - -2
(11,9) / 0
(8,6) / 0
(10,8) \ +1
(8,11) \ 0
(11,7) / -1
(13,9) \ 0
(11,11) \ 0
(14,11) - -1
(14,9) | +1
(13,12) | -1
(12,9) - +1
(16,9) / -2
(17,6) / -2
(11,12) - +1
(16,6) / -2
(16,8) | 0
(11,10) | +1
(10,9) \ +1
(11,8) / 0
(13,8) - -2
(15,6) / -2
(13,5) | +2
(10,7) \ +2
(14,6) \ 0
(10,6) | +2
(16,11) \ -2
(11,6) - +2
(11,5) | +2
(8,8) / +2
(8,14) / +2
(8,9) | -1
(7,9) - +2
(4,6) \ +2
(5,12) / +2
(13,4) / -2
(10,5) - +1
(10,11) \ 0
(7,8) / +2
(7,6) | +2
(7,11) - +1
(10,14) | -2
(9,14) / +2
(7,3) \ +2
(17,8) - -2
(14,5) \ +1
(11,14) - -1
(8,12) \ 0
(5,8) - +2
(8,13) | -1
(14,4) | +2
(8,5) / -1
(7,14) / +2
(8,3) \ +2
(6,6) - +2
(5,5) \ +2
(8,2) | +2
(6,4) / 0
(9,3) \ +1
(7,5) / 0
(5,3) \ +2
(6,3) - +1
(6,5) - +1
(6,2) | +2
(7,4) \ +1
(7,2) | +2
(5,4) \ +2
(5,6) | 0
(9,2) / -2
(10,2) - -2
(4,5) \ +2
(10,3) | +1
(3,6) / +2
(4,4) - +2
(2,6) - +2
(3,5) / +1
#</pre>

=== Multigame Run ===

The multigame simulation of completely random play includes a histogram showing game scores clustering in the 20's and 60's. This result is similar to results obtained by [http://www.morpionsolitaire.com/English/RecordsGrids5T.htm Jean-Jacques Sibil] (you will have to scroll down that page to find the reference) who has run nearly a billion such simulations achieving a high score of 102 as of 2010.

The following is a summary file:
<pre># --- Morpion Solitaire 5 (v1.7e) ---
#
#
# Command line options :
# Command line options :
# Summary of Morpion Configuration:
# Summary of Morpion Configuration:
# Variant (5T/5D) move validation = procedure ValidMove5T
# Variant (5T/D) move validation = procedure ValidMove5T
# Games to play = * unlimited *
# Games to play = * unlimited *
# Print (0=none,1=minimal,2=all) = 0
# Multi-game options:
# Multi-game options:
# - Status Updates = 500
# - Status Updates = 500
# - Keep best = 5
# - Keep best = 5
# - Keep worst = 0
# - Keep worst = 0
# - Histogram width = 80
# - Histogram grouping = 5
# Games will be saved = Yes
# - Format for game file (write) = procedure WriteMoveLogPS
# Replaying
# Replaying
# - Load recorded game file = "* None *"
# - Format for game file (read) = procedure ReadMoveLogPS
# - Game file to be replayed = * None *
# Player Strategy = procedure RandomPlayer
# Player Strategy = procedure RandomPlayer
# - Position Fitness Evaluator = "* None *"
# - Position Fitness Evaluator = * None *
# - Search Width (strategy dependant) = 5
# - Search Width (strategy dependant) = 5
# - Search Depth (strategy dependant) = 5
# - Search Depth (strategy dependant) = 5
# Log Details for analysis = No
#
#
Longest game 77 after 1 simulations &random=20122297.
# Running:
Longest game 77 after 85 simulations &random=274082001.
Longest game 78 after 118 simulations &random=559240181.
Longest game 78 after 123 simulations &random=1682993637.
Longest game 81 after 292 simulations &random=826134037.
Longest game 84 after 1181 simulations &random=1936506737.
Longest game 86 after 4584 simulations &random=1266457499.
Longest game 86 after 44424 simulations &random=1725594333.
Longest game 86 after 47918 simulations &random=1686351259.
Longest game 86 after 50600 simulations &random=665807725.
Longest game 87 after 60841 simulations &random=152917603.
Longest game 87 after 74778 simulations &random=1037682795.
Longest game 88 after 173368 simulations &random=72059739.
Longest game 88 after 241134 simulations &random=2095899781.


Results from Sample of 242921 games of Morpion 5T/D:
Longest game 66 after 1 simulations.
Longest game 69 after 3 simulations.
Longest game 69 after 9 simulations.
Longest game 70 after 23 simulations.
Longest game 70 after 42 simulations.
Longest game 74 after 110 simulations.
Longest game 77 after 167 simulations.
Longest game 78 after 223 simulations.
Longest game 79 after 357 simulations.
Longest game 81 after 555 simulations.
Longest game 82 after 887 simulations.
Longest game 82 after 2119 simulations.
Longest game 86 after 3092 simulations.

Results from Sample of 31434 games of Morpion 5T:
Shortest game was 20 moves.
Shortest game was 20 moves.
Average game was 53.63997582235795 moves.
Average game was 53.65064774144681 moves.
Longest game was 86 moves.
Longest game was 88 moves.
Average time/game is 205.6081313227715 ms.
Average time/game is 133.1678282239905 ms.
&random is now 452165683.


Summary of Results every '*' = 246 games
Summary of Results every '*' = 940 games
0 | ( 0)
0 | ( 0)
5 | ( 0)
20 | ( 7970) ********************************
40 | ( 3681) **************
10 | ( 0)
15 | ( 0)
60 | ( 19737) ********************************************************************************
20 | ( 38637) *****************************************
80 | ( 46)
25 | ( 13790) **************
30 | ( 6657) *******
35 | ( 2604) **
40 | ( 1306) *
45 | ( 1088) *
50 | ( 3448) ***
55 | ( 22481) ***********************
60 | ( 75207) ********************************************************************************
65 | ( 61501) *****************************************************************
70 | ( 13902) **************
75 | ( 1922) **
80 | ( 349)
85 | ( 29)


Longest (5) Game(s):
Longest (5) Game(s):
#
# Game Record for Morphion 5T game of 88 moves
# Date: 2012/02/18
# Saved: Games/5T/88-L0(241134)_20120218-083009-Fps.txt
# &random: 2095899781
# ReplayFile: * none * (* all * moves)
#


Morphion Solitare Grid (move=86):
Morphion Solitare Grid (move=88):
1234567890123456
1 ................
2 ....+.+.........
3 .....+++........
4 ..+++++++.......
5 .+++++++++......
6 ..++++****+.....
7 .+++++*++*+.....
8 ..++++*++*++....
9 ...****++****+..
10 ...*++++++++*+..
11 .++*++++++++*++.
12 ...****++****+..
13 ....++*++*++++..
14 .....+*++*+++...
15 .....+****+.....
16 .......++.......
17 ........+.......
18 ................
1234567890123456
#
# Game Record for Morphion 5T game of 88 moves
# Date: 2012/02/18
# Saved: Games/5T/88-L0(173368)_20120218-083009-Fps.txt
# &random: 72059739
# ReplayFile: * none * (* all * moves)
#


Morphion Solitare Grid (move=88):
1234567890123456
1234567890123456
1 ................
1 ................
2 .....+....+.....
2 .......+........
3 .....++++++.....
3 ......++...+....
4 .....+****+.....
4 .....+****+.....
5 ..++++*++*++....
5 ..++++*++*+.....
6 .+++++*++*++++..
6 ..++++*++*+++...
7 ..+****++****+..
7 ...****++****+..
8 ...*++++++++*+..
8 ...*++++++++*+..
9 ...*++++++++*+..
9 .++*++++++++*+..
10 ..+****++****+..
10 ...****++****+..
11 ..+..+*++*++++..
11 ..++++*++*+++++.
12 ....++*++*++++..
12 .+...+*++*+++++.
13 ......****+++++.
13 .....+****++++..
14 .....+...++++++.
14 ....+..+++++++..
15 .........++.....
15 .........+++++..
16 .........+......
16 ..........+.....
17 ................
17 ...........+....
18 ................
1234567890123456
1234567890123456

#
#
# Game Record for Morphion 5T game of 86 moves
# Game Record for Morphion 5T game of 87 moves
# Date: 2012/02/18
# Reference: Games/86-L0(3092)_20120208-090217.txt
# Saved: Games/5T/87-L0(74778)_20120218-083009-Fps.txt
# &random: 913514172
# &random: 1037682795
# Syntax for recorded games:
# ReplayFile: * none * (* all * moves)
# 1. whitespace and comments (everything past #) are ignored
# 2. moves may be ; or newline separated
# 3. moves are formatted (using BNF style variables like <x>):
# <movenumber>:<row>,<col>[^<drow>,<dcol>]
# The optional drow/dcol values provide an additional point
# used to disambiguate moves. The point drow/dcol must be
# chosen at one end of the line.
# Moves are applied in sorted order.
# 4. all row/col coordinates are relative to the 1,1 origin
# located at the intersection of the lines containing the
# top and left most edges of the cross.
#
#
1:5,7^1,7;2:10,8^10,4;3:3,1^7,1;4:3,3^1,5;5:3,8^1,6;6:4,6^4,10;7:4,5^4,2;
8:5,4^1,4;9:7,0^7,4;10:6,4^5,4;11:3,10^7,10;12:7,11^7,7;13:6,7^5,7;14:5,2^3,4;
15:8,8^6,10;16:5,6^3,4;17:5,5^3,7;18:1,3^1,7;19:3,5^1,3;20:2,5^1,5;21:5,9^3,7;
22:5,3^5,1;23:5,8^5,5;24:7,6^3,10;25:3,6^3,3;26:1,8^5,4;27:2,0^6,4;28:2,3^1,3;
29:6,6^3,3;30:7,5^4,8;31:6,8^3,8;32:8,3^6,1;33:2,6^1,6;34:2,8^2,4;35:0,8^4,4;
36:0,3^4,7;37:8,6^6,4;38:3,11^7,7;39:9,6^5,6;40:6,5^3,8;41:3,2^7,6;42:8,5^8,3;
43:6,2^3,2;44:9,5^6,5;45:8,0^4,4;46:11,7^7,3;47:-1,8^3,8;48:6,3^6,2;49:9,3^5,3;
50:9,2^9,6;51:6,9^10,5;52:8,11^4,7;53:3,9^3,7;54:6,11^6,7;55:0,5^4,1;56:0,6^4,10;
57:0,7^-1,8;58:2,9^6,9;59:0,4^0,3;60:4,11^0,7;61:5,11^3,11;62:-1,3^3,7;
63:11,3^7,7;64:3,0^7,4;65:3,-1^3,3;66:4,0^3,-1;67:11,8^7,4;68:2,2^0,4;69:9,8^7,8;
70:2,1^2,0;71:8,9^6,11;72:8,10^8,7;73:9,9^7,11;74:9,11^5,7;75:10,9^6,9;
76:11,10^7,6;77:9,10^9,7;78:12,7^8,11;79:10,10^7,10;80:13,7^9,7;81:11,11^7,7;
82:11,9^11,7;83:12,8^9,11;84:10,11^7,11;85:10,12^10,8;86:11,12^7,8;

Move Log
1 : "|" [5,7] : [1,7] [2,7] [3,7] [4,7] [5,7] - of 4 choices.
2 : "-" [10,8] : [10,4] [10,5] [10,6] [10,7] [10,8] - of 27 choices.
3 : "|" [3,1] : [3,1] [4,1] [5,1] [6,1] [7,1] - of 25 choices.
4 : "/" [3,3] : [1,5] [2,4] [3,3] [4,2] [5,1] - of 23 choices.
5 : "\" [3,8] : [1,6] [2,7] [3,8] [4,9] [5,10] - of 22 choices.
6 : "-" [4,6] : [4,6] [4,7] [4,8] [4,9] [4,10] - of 21 choices.
7 : "-" [4,5] : [4,2] [4,3] [4,4] [4,5] [4,6] - of 20 choices.
8 : "|" [5,4] : [1,4] [2,4] [3,4] [4,4] [5,4] - of 17 choices.
9 : "-" [7,0] : [7,0] [7,1] [7,2] [7,3] [7,4] - of 16 choices.
10 : "|" [6,4] : [5,4] [6,4] [7,4] [8,4] [9,4] - of 15 choices.
11 : "|" [3,10] : [3,10] [4,10] [5,10] [6,10] [7,10] - of 13 choices.
12 : "-" [7,11] : [7,7] [7,8] [7,9] [7,10] [7,11] - of 11 choices.
13 : "|" [6,7] : [5,7] [6,7] [7,7] [8,7] [9,7] - of 10 choices.
14 : "/" [5,2] : [3,4] [4,3] [5,2] [6,1] [7,0] - of 8 choices.
15 : "/" [8,8] : [6,10] [7,9] [8,8] [9,7] [10,6] - of 7 choices.
16 : "\" [5,6] : [3,4] [4,5] [5,6] [6,7] [7,8] - of 6 choices.
17 : "/" [5,5] : [3,7] [4,6] [5,5] [6,4] [7,3] - of 6 choices.
18 : "-" [1,3] : [1,3] [1,4] [1,5] [1,6] [1,7] - of 11 choices.
19 : "\" [3,5] : [1,3] [2,4] [3,5] [4,6] [5,7] - of 10 choices.
20 : "|" [2,5] : [1,5] [2,5] [3,5] [4,5] [5,5] - of 13 choices.
21 : "\" [5,9] : [3,7] [4,8] [5,9] [6,10] [7,11] - of 12 choices.
22 : "-" [5,3] : [5,1] [5,2] [5,3] [5,4] [5,5] - of 13 choices.
23 : "-" [5,8] : [5,5] [5,6] [5,7] [5,8] [5,9] - of 15 choices.
24 : "/" [7,6] : [3,10] [4,9] [5,8] [6,7] [7,6] - of 18 choices.
25 : "-" [3,6] : [3,3] [3,4] [3,5] [3,6] [3,7] - of 17 choices.
26 : "/" [1,8] : [1,8] [2,7] [3,6] [4,5] [5,4] - of 21 choices.
27 : "\" [2,0] : [2,0] [3,1] [4,2] [5,3] [6,4] - of 19 choices.
28 : "|" [2,3] : [1,3] [2,3] [3,3] [4,3] [5,3] - of 17 choices.
29 : "\" [6,6] : [3,3] [4,4] [5,5] [6,6] [7,7] - of 16 choices.
30 : "/" [7,5] : [4,8] [5,7] [6,6] [7,5] [8,4] - of 16 choices.
31 : "|" [6,8] : [3,8] [4,8] [5,8] [6,8] [7,8] - of 16 choices.
32 : "\" [8,3] : [6,1] [7,2] [8,3] [9,4] [10,5] - of 17 choices.
33 : "|" [2,6] : [1,6] [2,6] [3,6] [4,6] [5,6] - of 17 choices.
34 : "-" [2,8] : [2,4] [2,5] [2,6] [2,7] [2,8] - of 16 choices.
35 : "/" [0,8] : [0,8] [1,7] [2,6] [3,5] [4,4] - of 14 choices.
36 : "\" [0,3] : [0,3] [1,4] [2,5] [3,6] [4,7] - of 12 choices.
37 : "\" [8,6] : [6,4] [7,5] [8,6] [9,7] [10,8] - of 9 choices.
38 : "/" [3,11] : [3,11] [4,10] [5,9] [6,8] [7,7] - of 13 choices.
39 : "|" [9,6] : [5,6] [6,6] [7,6] [8,6] [9,6] - of 11 choices.
40 : "/" [6,5] : [3,8] [4,7] [5,6] [6,5] [7,4] - of 12 choices.
41 : "\" [3,2] : [3,2] [4,3] [5,4] [6,5] [7,6] - of 12 choices.
42 : "-" [8,5] : [8,3] [8,4] [8,5] [8,6] [8,7] - of 13 choices.
43 : "|" [6,2] : [3,2] [4,2] [5,2] [6,2] [7,2] - of 17 choices.
44 : "|" [9,5] : [6,5] [7,5] [8,5] [9,5] [10,5] - of 22 choices.
45 : "/" [8,0] : [4,4] [5,3] [6,2] [7,1] [8,0] - of 22 choices.
46 : "\" [11,7] : [7,3] [8,4] [9,5] [10,6] [11,7] - of 21 choices.
47 : "|" [-1,8] : [-1,8] [0,8] [1,8] [2,8] [3,8] - of 19 choices.
48 : "-" [6,3] : [6,2] [6,3] [6,4] [6,5] [6,6] - of 19 choices.
49 : "|" [9,3] : [5,3] [6,3] [7,3] [8,3] [9,3] - of 14 choices.
50 : "-" [9,2] : [9,2] [9,3] [9,4] [9,5] [9,6] - of 13 choices.
51 : "/" [6,9] : [6,9] [7,8] [8,7] [9,6] [10,5] - of 11 choices.
52 : "\" [8,11] : [4,7] [5,8] [6,9] [7,10] [8,11] - of 11 choices.
53 : "-" [3,9] : [3,7] [3,8] [3,9] [3,10] [3,11] - of 10 choices.
54 : "-" [6,11] : [6,7] [6,8] [6,9] [6,10] [6,11] - of 11 choices.
55 : "/" [0,5] : [0,5] [1,4] [2,3] [3,2] [4,1] - of 10 choices.
56 : "\" [0,6] : [0,6] [1,7] [2,8] [3,9] [4,10] - of 8 choices.
57 : "/" [0,7] : [-1,8] [0,7] [1,6] [2,5] [3,4] - of 6 choices.
58 : "|" [2,9] : [2,9] [3,9] [4,9] [5,9] [6,9] - of 8 choices.
59 : "-" [0,4] : [0,3] [0,4] [0,5] [0,6] [0,7] - of 8 choices.
60 : "\" [4,11] : [0,7] [1,8] [2,9] [3,10] [4,11] - of 6 choices.
61 : "|" [5,11] : [3,11] [4,11] [5,11] [6,11] [7,11] - of 6 choices.
62 : "\" [-1,3] : [-1,3] [0,4] [1,5] [2,6] [3,7] - of 4 choices.
63 : "/" [11,3] : [7,7] [8,6] [9,5] [10,4] [11,3] - of 3 choices.
64 : "\" [3,0] : [3,0] [4,1] [5,2] [6,3] [7,4] - of 2 choices.
65 : "-" [3,-1] : [3,-1] [3,0] [3,1] [3,2] [3,3] - of 2 choices.
66 : "\" [4,0] : [3,-1] [4,0] [5,1] [6,2] [7,3] - of 2 choices.
67 : "\" [11,8] : [7,4] [8,5] [9,6] [10,7] [11,8] - of 2 choices.
68 : "/" [2,2] : [0,4] [1,3] [2,2] [3,1] [4,0] - of 2 choices.
69 : "|" [9,8] : [7,8] [8,8] [9,8] [10,8] [11,8] - of 2 choices.
70 : "-" [2,1] : [2,0] [2,1] [2,2] [2,3] [2,4] - of 2 choices.
71 : "/" [8,9] : [6,11] [7,10] [8,9] [9,8] [10,7] - of 1 choices.
72 : "-" [8,10] : [8,7] [8,8] [8,9] [8,10] [8,11] - of 1 choices.
73 : "/" [9,9] : [7,11] [8,10] [9,9] [10,8] [11,7] - of 2 choices.
74 : "\" [9,11] : [5,7] [6,8] [7,9] [8,10] [9,11] - of 3 choices.
75 : "|" [10,9] : [6,9] [7,9] [8,9] [9,9] [10,9] - of 3 choices.
76 : "\" [11,10] : [7,6] [8,7] [9,8] [10,9] [11,10] - of 3 choices.
77 : "-" [9,10] : [9,7] [9,8] [9,9] [9,10] [9,11] - of 2 choices.
78 : "/" [12,7] : [8,11] [9,10] [10,9] [11,8] [12,7] - of 3 choices.
79 : "|" [10,10] : [7,10] [8,10] [9,10] [10,10] [11,10] - of 2 choices.
80 : "|" [13,7] : [9,7] [10,7] [11,7] [12,7] [13,7] - of 2 choices.
81 : "\" [11,11] : [7,7] [8,8] [9,9] [10,10] [11,11] - of 1 choices.
82 : "-" [11,9] : [11,7] [11,8] [11,9] [11,10] [11,11] - of 2 choices.
83 : "/" [12,8] : [9,11] [10,10] [11,9] [12,8] [13,7] - of 2 choices.
84 : "|" [10,11] : [7,11] [8,11] [9,11] [10,11] [11,11] - of 1 choices.
85 : "-" [10,12] : [10,8] [10,9] [10,10] [10,11] [10,12] - of 2 choices.
86 : "\" [11,12] : [7,8] [8,9] [9,10] [10,11] [11,12] - of 1 choices.

Morphion Solitare Grid (move=82):

12345678901234567
1 .................
2 ....+.+..........
3 ....++++++++.....
4 ..+++++****......
5 ..+++++*++*......
6 ..+++++*++*+.....
7 ..++****++****...
8 .+++*++++++++*...
9 ....*++++++++*...
10 ...+****++****+..
11 ..+++++*++*++++..
12 ......+*++*++..+.
13 .......****+.....
14 .......++++++....
15 .........+.......
16 .................
12345678901234567


Morphion Solitare Grid (move=87):
123456789012345
1 ...............
2 ....+.+........
3 .....+.........
4 ....+++++......
5 ...++++++......
6 .+++++****+....
7 ..++++*++*+....
8 .+++++*++*++...
9 ..+****++****..
10 ..+*++++++++*+.
11 ...*++++++++*..
12 ..+****++****+.
13 ...+++*++*++++.
14 ..++++*++*++...
15 .....+****+.+..
16 ......+++++....
17 ........+......
18 ...............
123456789012345
#
#
# Game Record for Morphion 5T game of 82 moves
# Game Record for Morphion 5T game of 87 moves
# Date: 2012/02/18
# Reference: Games/82-L0(2119)_20120208-090217.txt
# Saved: Games/5T/87-L0(60841)_20120218-083009-Fps.txt
# &random: 2066669156
# &random: 152917603
# Syntax for recorded games:
# ReplayFile: * none * (* all * moves)
# 1. whitespace and comments (everything past #) are ignored
# 2. moves may be ; or newline separated
# 3. moves are formatted (using BNF style variables like <x>):
# <movenumber>:<row>,<col>[^<drow>,<dcol>]
# The optional drow/dcol values provide an additional point
# used to disambiguate moves. The point drow/dcol must be
# chosen at one end of the line.
# Moves are applied in sorted order.
# 4. all row/col coordinates are relative to the 1,1 origin
# located at the intersection of the lines containing the
# top and left most edges of the cross.
#
#
1:0,7^4,7;2:3,8^1,6;3:8,1^4,1;4:11,7^7,7;5:9,5^7,3;6:8,8^6,10;7:7,11^7,7;
8:8,3^6,1;9:4,0^4,4;10:7,5^7,1;11:5,9^3,7;12:3,3^1,5;13:8,10^4,10;14:2,5^0,7;
15:11,4^7,4;16:10,8^10,4;17:1,3^1,7;18:9,9^7,11;19:4,6^4,10;20:9,6^7,8;
21:9,8^9,5;22:11,8^7,8;23:8,5^7,4;24:8,6^8,3;25:11,9^7,5;26:6,8^5,9;27:0,4^4,4;
28:5,8^3,8;29:3,6^1,4;30:5,7^4,6;31:5,6^5,10;32:11,5^7,5;33:2,6^1,6;34:3,5^3,4;
35:-1,3^3,7;36:2,3^2,7;37:6,5^4,7;38:11,6^11,5;39:12,6^8,6;40:0,8^4,4;41:0,3^4,3;
42:8,9^7,10;43:0,2^4,6;44:6,9^4,9;45:8,11^8,7;46:6,6^4,8;47:5,5^9,9;48:6,7^6,6;
49:6,4^3,7;50:4,5^8,9;51:0,5^4,5;52:7,6^5,8;53:5,4^9,8;54:6,3^3,6;55:9,12^5,8;
56:0,6^0,2;57:3,2^0,5;58:5,3^4,3;59:3,1^7,5;60:6,2^6,6;61:3,-1^7,3;62:3,0^3,-1;
63:5,2^5,6;64:2,2^0,4;65:1,1^5,5;66:2,-1^6,3;67:2,0^-1,3;68:1,2^0,2;69:8,2^4,2;
70:9,3^8,2;71:8,0^4,4;72:2,1^2,-1;73:0,1^4,5;74:4,-1^0,3;75:1,0^5,4;76:5,0^1,0;
77:1,-1^1,3;78:8,-1^8,3;79:5,-1^1,-1;80:-1,1^3,1;81:7,0^4,3;82:5,-2^5,2;

Move Log
1 : "|" [0,7] : [0,7] [1,7] [2,7] [3,7] [4,7] - of 4 choices.
2 : "\" [3,8] : [1,6] [2,7] [3,8] [4,9] [5,10] - of 27 choices.
3 : "|" [8,1] : [4,1] [5,1] [6,1] [7,1] [8,1] - of 26 choices.
4 : "|" [11,7] : [7,7] [8,7] [9,7] [10,7] [11,7] - of 24 choices.
5 : "\" [9,5] : [7,3] [8,4] [9,5] [10,6] [11,7] - of 23 choices.
6 : "/" [8,8] : [6,10] [7,9] [8,8] [9,7] [10,6] - of 22 choices.
7 : "-" [7,11] : [7,7] [7,8] [7,9] [7,10] [7,11] - of 21 choices.
8 : "\" [8,3] : [6,1] [7,2] [8,3] [9,4] [10,5] - of 20 choices.
9 : "-" [4,0] : [4,0] [4,1] [4,2] [4,3] [4,4] - of 19 choices.
10 : "-" [7,5] : [7,1] [7,2] [7,3] [7,4] [7,5] - of 17 choices.
11 : "\" [5,9] : [3,7] [4,8] [5,9] [6,10] [7,11] - of 15 choices.
12 : "/" [3,3] : [1,5] [2,4] [3,3] [4,2] [5,1] - of 14 choices.
13 : "|" [8,10] : [4,10] [5,10] [6,10] [7,10] [8,10] - of 13 choices.
14 : "/" [2,5] : [0,7] [1,6] [2,5] [3,4] [4,3] - of 11 choices.
15 : "|" [11,4] : [7,4] [8,4] [9,4] [10,4] [11,4] - of 10 choices.
16 : "-" [10,8] : [10,4] [10,5] [10,6] [10,7] [10,8] - of 9 choices.
17 : "-" [1,3] : [1,3] [1,4] [1,5] [1,6] [1,7] - of 8 choices.
18 : "/" [9,9] : [7,11] [8,10] [9,9] [10,8] [11,7] - of 6 choices.
19 : "-" [4,6] : [4,6] [4,7] [4,8] [4,9] [4,10] - of 5 choices.
20 : "/" [9,6] : [7,8] [8,7] [9,6] [10,5] [11,4] - of 3 choices.
21 : "-" [9,8] : [9,5] [9,6] [9,7] [9,8] [9,9] - of 5 choices.
22 : "|" [11,8] : [7,8] [8,8] [9,8] [10,8] [11,8] - of 4 choices.
23 : "\" [8,5] : [7,4] [8,5] [9,6] [10,7] [11,8] - of 3 choices.
24 : "-" [8,6] : [8,3] [8,4] [8,5] [8,6] [8,7] - of 7 choices.
25 : "\" [11,9] : [7,5] [8,6] [9,7] [10,8] [11,9] - of 10 choices.
26 : "/" [6,8] : [5,9] [6,8] [7,7] [8,6] [9,5] - of 8 choices.
27 : "|" [0,4] : [0,4] [1,4] [2,4] [3,4] [4,4] - of 6 choices.
28 : "|" [5,8] : [3,8] [4,8] [5,8] [6,8] [7,8] - of 4 choices.
29 : "\" [3,6] : [1,4] [2,5] [3,6] [4,7] [5,8] - of 4 choices.
30 : "\" [5,7] : [4,6] [5,7] [6,8] [7,9] [8,10] - of 5 choices.
31 : "-" [5,6] : [5,6] [5,7] [5,8] [5,9] [5,10] - of 7 choices.
32 : "|" [11,5] : [7,5] [8,5] [9,5] [10,5] [11,5] - of 8 choices.
33 : "|" [2,6] : [1,6] [2,6] [3,6] [4,6] [5,6] - of 8 choices.
34 : "-" [3,5] : [3,4] [3,5] [3,6] [3,7] [3,8] - of 10 choices.
35 : "\" [-1,3] : [-1,3] [0,4] [1,5] [2,6] [3,7] - of 11 choices.
36 : "-" [2,3] : [2,3] [2,4] [2,5] [2,6] [2,7] - of 10 choices.
37 : "/" [6,5] : [4,7] [5,6] [6,5] [7,4] [8,3] - of 11 choices.
38 : "-" [11,6] : [11,5] [11,6] [11,7] [11,8] [11,9] - of 9 choices.
39 : "|" [12,6] : [8,6] [9,6] [10,6] [11,6] [12,6] - of 10 choices.
40 : "/" [0,8] : [0,8] [1,7] [2,6] [3,5] [4,4] - of 8 choices.
41 : "|" [0,3] : [0,3] [1,3] [2,3] [3,3] [4,3] - of 6 choices.
42 : "/" [8,9] : [7,10] [8,9] [9,8] [10,7] [11,6] - of 3 choices.
43 : "\" [0,2] : [0,2] [1,3] [2,4] [3,5] [4,6] - of 6 choices.
44 : "|" [6,9] : [4,9] [5,9] [6,9] [7,9] [8,9] - of 5 choices.
45 : "-" [8,11] : [8,7] [8,8] [8,9] [8,10] [8,11] - of 2 choices.
46 : "/" [6,6] : [4,8] [5,7] [6,6] [7,5] [8,4] - of 2 choices.
47 : "\" [5,5] : [5,5] [6,6] [7,7] [8,8] [9,9] - of 7 choices.
48 : "-" [6,7] : [6,6] [6,7] [6,8] [6,9] [6,10] - of 7 choices.
49 : "/" [6,4] : [3,7] [4,6] [5,5] [6,4] [7,3] - of 11 choices.
50 : "\" [4,5] : [4,5] [5,6] [6,7] [7,8] [8,9] - of 10 choices.
51 : "|" [0,5] : [0,5] [1,5] [2,5] [3,5] [4,5] - of 4 choices.
52 : "/" [7,6] : [5,8] [6,7] [7,6] [8,5] [9,4] - of 8 choices.
53 : "\" [5,4] : [5,4] [6,5] [7,6] [8,7] [9,8] - of 9 choices.
54 : "/" [6,3] : [3,6] [4,5] [5,4] [6,3] [7,2] - of 10 choices.
55 : "\" [9,12] : [5,8] [6,9] [7,10] [8,11] [9,12] - of 9 choices.
56 : "-" [0,6] : [0,2] [0,3] [0,4] [0,5] [0,6] - of 8 choices.
57 : "/" [3,2] : [0,5] [1,4] [2,3] [3,2] [4,1] - of 4 choices.
58 : "|" [5,3] : [4,3] [5,3] [6,3] [7,3] [8,3] - of 3 choices.
59 : "\" [3,1] : [3,1] [4,2] [5,3] [6,4] [7,5] - of 5 choices.
60 : "-" [6,2] : [6,2] [6,3] [6,4] [6,5] [6,6] - of 6 choices.
61 : "\" [3,-1] : [3,-1] [4,0] [5,1] [6,2] [7,3] - of 7 choices.
62 : "-" [3,0] : [3,-1] [3,0] [3,1] [3,2] [3,3] - of 7 choices.
63 : "-" [5,2] : [5,2] [5,3] [5,4] [5,5] [5,6] - of 6 choices.
64 : "/" [2,2] : [0,4] [1,3] [2,2] [3,1] [4,0] - of 5 choices.
65 : "\" [1,1] : [1,1] [2,2] [3,3] [4,4] [5,5] - of 6 choices.
66 : "\" [2,-1] : [2,-1] [3,0] [4,1] [5,2] [6,3] - of 6 choices.
67 : "/" [2,0] : [-1,3] [0,2] [1,1] [2,0] [3,-1] - of 5 choices.
68 : "|" [1,2] : [0,2] [1,2] [2,2] [3,2] [4,2] - of 5 choices.
69 : "|" [8,2] : [4,2] [5,2] [6,2] [7,2] [8,2] - of 4 choices.
70 : "\" [9,3] : [8,2] [9,3] [10,4] [11,5] [12,6] - of 5 choices.
71 : "/" [8,0] : [4,4] [5,3] [6,2] [7,1] [8,0] - of 3 choices.
72 : "-" [2,1] : [2,-1] [2,0] [2,1] [2,2] [2,3] - of 3 choices.
73 : "\" [0,1] : [0,1] [1,2] [2,3] [3,4] [4,5] - of 6 choices.
74 : "/" [4,-1] : [0,3] [1,2] [2,1] [3,0] [4,-1] - of 5 choices.
75 : "\" [1,0] : [1,0] [2,1] [3,2] [4,3] [5,4] - of 3 choices.
76 : "|" [5,0] : [1,0] [2,0] [3,0] [4,0] [5,0] - of 5 choices.
77 : "-" [1,-1] : [1,-1] [1,0] [1,1] [1,2] [1,3] - of 3 choices.
78 : "-" [8,-1] : [8,-1] [8,0] [8,1] [8,2] [8,3] - of 4 choices.
79 : "|" [5,-1] : [1,-1] [2,-1] [3,-1] [4,-1] [5,-1] - of 4 choices.
80 : "|" [-1,1] : [-1,1] [0,1] [1,1] [2,1] [3,1] - of 3 choices.
81 : "/" [7,0] : [4,3] [5,2] [6,1] [7,0] [8,-1] - of 2 choices.
82 : "-" [5,-2] : [5,-2] [5,-1] [5,0] [5,1] [5,2] - of 1 choices.

Morphion Solitare Grid (move=82):

1234567890123456789
1 ...................
2 .........+.++......
3 ........****++.....
4 ........*++*+++....
5 .......+*++*++++...
6 .....****++****+++.
7 ....+*++++++++*+++.
8 ...++*++++++++*+...
9 ..+.+****++****+...
10 .+++++++*++*++++...
11 ....++++*++*++..+..
12 ....++++****+......
13 ......++...+.......
14 ......+.+..........
15 ...................
1234567890123456789


Morphion Solitare Grid (move=87):
123456789012345678
1 ..................
2 .........+.+..+...
3 .....+...++++++...
4 ......****+++++...
5 .....+*++*+++++...
6 ....++*++*+++++++.
7 ..+****++****+.+..
8 .++*++++++++*+....
9 .++*++++++++*+....
10 ..+****++****+....
11 .+++++*++*++++....
12 ....++*++*++......
13 ....++****........
14 ....+++...........
15 ..................
123456789012345678
#
#
# Game Record for Morphion 5T game of 82 moves
# Game Record for Morphion 5T game of 86 moves
# Date: 2012/02/18
# Reference: Games/82-L0(887)_20120208-090217.txt
# Saved: Games/5T/86-L0(50600)_20120218-083009-Fps.txt
# &random: 1478604792
# &random: 665807725
# Syntax for recorded games:
# ReplayFile: * none * (* all * moves)
# 1. whitespace and comments (everything past #) are ignored
# 2. moves may be ; or newline separated
# 3. moves are formatted (using BNF style variables like <x>):
# <movenumber>:<row>,<col>[^<drow>,<dcol>]
# The optional drow/dcol values provide an additional point
# used to disambiguate moves. The point drow/dcol must be
# chosen at one end of the line.
# Moves are applied in sorted order.
# 4. all row/col coordinates are relative to the 1,1 origin
# located at the intersection of the lines containing the
# top and left most edges of the cross.
#
#
1:0,7^4,7;2:2,5^0,7;3:4,5^4,1;4:10,8^10,4;5:4,6^4,5;6:11,7^7,7;7:3,8^1,6;
8:8,8^6,10;9:7,11^7,7;10:1,8^1,4;11:3,3^1,5;12:6,4^10,4;13:5,9^3,7;14:8,10^4,10;
15:7,0^7,4;16:5,4^2,4;17:3,6^1,8;18:5,8^1,4;19:2,8^1,8;20:8,1^4,1;21:5,5^2,8;
22:6,6^3,3;23:3,5^3,3;24:0,5^4,5;25:9,9^7,11;26:9,5^7,3;27:2,6^2,4;28:0,8^4,4;
29:5,6^1,6;30:6,7^3,4;31:8,3^6,1;32:6,5^4,7;33:5,7^5,4;34:6,3^6,7;35:5,3^3,3;
36:9,0^5,4;37:6,8^4,6;38:9,8^5,8;39:9,6^9,4;40:3,11^7,7;41:7,6^4,3;42:6,9^5,10;
43:8,6^5,6;44:3,10^7,6;45:8,9^5,9;46:7,5^5,3;47:8,5^4,5;48:8,11^8,7;49:6,11^6,7;
50:8,2^8,6;51:11,3^7,7;52:3,9^3,7;53:5,11^1,7;54:4,11^8,11;55:5,12^5,8;
56:4,13^8,9;57:5,2^4,1;58:2,9^0,7;59:2,10^6,6;60:9,12^5,8;61:4,12^4,9;62:5,0^5,4;
63:1,9^5,9;64:6,2^4,2;65:5,13^1,9;66:8,-1^4,3;67:8,0^4,4;68:6,0^5,0;69:8,-2^8,2;
70:9,3^6,0;71:10,2^6,6;72:10,3^7,3;73:6,-1^6,3;74:9,2^6,-1;75:11,2^7,6;
76:9,1^9,0;77:10,0^6,4;78:12,2^8,2;79:10,1^10,0;80:12,4^8,0;81:7,-2^11,2;
82:8,-3^4,1;


Morphion Solitare Grid (move=86):
Move Log
1234567890123456
1 : "|" [0,7] : [0,7] [1,7] [2,7] [3,7] [4,7] - of 4 choices.
1 ................
2 : "/" [2,5] : [0,7] [1,6] [2,5] [3,4] [4,3] - of 27 choices.
2 .....++..+++....
3 : "-" [4,5] : [4,1] [4,2] [4,3] [4,4] [4,5] - of 26 choices.
3 .....+****+.....
4 : "-" [10,8] : [10,4] [10,5] [10,6] [10,7] [10,8] - of 25 choices.
4 ....+.*++*+.....
5 : "-" [4,6] : [4,5] [4,6] [4,7] [4,8] [4,9] - of 23 choices.
5 ...+.+*++*+..+..
6 : "|" [11,7] : [7,7] [8,7] [9,7] [10,7] [11,7] - of 20 choices.
6 ..+****++****...
7 : "\" [3,8] : [1,6] [2,7] [3,8] [4,9] [5,10] - of 19 choices.
7 ..+*++++++++*...
8 : "/" [8,8] : [6,10] [7,9] [8,8] [9,7] [10,6] - of 18 choices.
8 ..+*++++++++*...
9 : "-" [7,11] : [7,7] [7,8] [7,9] [7,10] [7,11] - of 17 choices.
9 ..+****++****++.
10 : "-" [1,8] : [1,4] [1,5] [1,6] [1,7] [1,8] - of 16 choices.
10 .+++++*++*+++++.
11 : "/" [3,3] : [1,5] [2,4] [3,3] [4,2] [5,1] - of 14 choices.
11 ....++*++*+++++.
12 : "|" [6,4] : [6,4] [7,4] [8,4] [9,4] [10,4] - of 13 choices.
12 .....+****+++++.
13 : "\" [5,9] : [3,7] [4,8] [5,9] [6,10] [7,11] - of 13 choices.
13 .....++++++++++.
14 : "|" [8,10] : [4,10] [5,10] [6,10] [7,10] [8,10] - of 12 choices.
14 ..........+++++.
15 : "-" [7,0] : [7,0] [7,1] [7,2] [7,3] [7,4] - of 11 choices.
15 ..........+.....
16 : "|" [5,4] : [2,4] [3,4] [4,4] [5,4] [6,4] - of 9 choices.
16 ................
17 : "/" [3,6] : [1,8] [2,7] [3,6] [4,5] [5,4] - of 7 choices.
1234567890123456
18 : "\" [5,8] : [1,4] [2,5] [3,6] [4,7] [5,8] - of 10 choices.
19 : "|" [2,8] : [1,8] [2,8] [3,8] [4,8] [5,8] - of 11 choices.
20 : "|" [8,1] : [4,1] [5,1] [6,1] [7,1] [8,1] - of 10 choices.
21 : "/" [5,5] : [2,8] [3,7] [4,6] [5,5] [6,4] - of 8 choices.
22 : "\" [6,6] : [3,3] [4,4] [5,5] [6,6] [7,7] - of 9 choices.
23 : "-" [3,5] : [3,3] [3,4] [3,5] [3,6] [3,7] - of 7 choices.
24 : "|" [0,5] : [0,5] [1,5] [2,5] [3,5] [4,5] - of 6 choices.
25 : "/" [9,9] : [7,11] [8,10] [9,9] [10,8] [11,7] - of 4 choices.
26 : "\" [9,5] : [7,3] [8,4] [9,5] [10,6] [11,7] - of 3 choices.
27 : "-" [2,6] : [2,4] [2,5] [2,6] [2,7] [2,8] - of 2 choices.
28 : "/" [0,8] : [0,8] [1,7] [2,6] [3,5] [4,4] - of 6 choices.
29 : "|" [5,6] : [1,6] [2,6] [3,6] [4,6] [5,6] - of 4 choices.
30 : "\" [6,7] : [3,4] [4,5] [5,6] [6,7] [7,8] - of 6 choices.
31 : "\" [8,3] : [6,1] [7,2] [8,3] [9,4] [10,5] - of 5 choices.
32 : "/" [6,5] : [4,7] [5,6] [6,5] [7,4] [8,3] - of 5 choices.
33 : "-" [5,7] : [5,4] [5,5] [5,6] [5,7] [5,8] - of 6 choices.
34 : "-" [6,3] : [6,3] [6,4] [6,5] [6,6] [6,7] - of 8 choices.
35 : "|" [5,3] : [3,3] [4,3] [5,3] [6,3] [7,3] - of 9 choices.
36 : "/" [9,0] : [5,4] [6,3] [7,2] [8,1] [9,0] - of 7 choices.
37 : "\" [6,8] : [4,6] [5,7] [6,8] [7,9] [8,10] - of 6 choices.
38 : "|" [9,8] : [5,8] [6,8] [7,8] [8,8] [9,8] - of 8 choices.
39 : "-" [9,6] : [9,4] [9,5] [9,6] [9,7] [9,8] - of 9 choices.
40 : "/" [3,11] : [3,11] [4,10] [5,9] [6,8] [7,7] - of 11 choices.
41 : "\" [7,6] : [4,3] [5,4] [6,5] [7,6] [8,7] - of 7 choices.
42 : "/" [6,9] : [5,10] [6,9] [7,8] [8,7] [9,6] - of 10 choices.
43 : "|" [8,6] : [5,6] [6,6] [7,6] [8,6] [9,6] - of 11 choices.
44 : "/" [3,10] : [3,10] [4,9] [5,8] [6,7] [7,6] - of 16 choices.
45 : "|" [8,9] : [5,9] [6,9] [7,9] [8,9] [9,9] - of 14 choices.
46 : "\" [7,5] : [5,3] [6,4] [7,5] [8,6] [9,7] - of 14 choices.
47 : "|" [8,5] : [4,5] [5,5] [6,5] [7,5] [8,5] - of 15 choices.
48 : "-" [8,11] : [8,7] [8,8] [8,9] [8,10] [8,11] - of 13 choices.
49 : "-" [6,11] : [6,7] [6,8] [6,9] [6,10] [6,11] - of 13 choices.
50 : "-" [8,2] : [8,2] [8,3] [8,4] [8,5] [8,6] - of 12 choices.
51 : "/" [11,3] : [7,7] [8,6] [9,5] [10,4] [11,3] - of 10 choices.
52 : "-" [3,9] : [3,7] [3,8] [3,9] [3,10] [3,11] - of 9 choices.
53 : "\" [5,11] : [1,7] [2,8] [3,9] [4,10] [5,11] - of 10 choices.
54 : "|" [4,11] : [4,11] [5,11] [6,11] [7,11] [8,11] - of 12 choices.
55 : "-" [5,12] : [5,8] [5,9] [5,10] [5,11] [5,12] - of 10 choices.
56 : "/" [4,13] : [4,13] [5,12] [6,11] [7,10] [8,9] - of 10 choices.
57 : "\" [5,2] : [4,1] [5,2] [6,3] [7,4] [8,5] - of 9 choices.
58 : "\" [2,9] : [0,7] [1,8] [2,9] [3,10] [4,11] - of 9 choices.
59 : "/" [2,10] : [2,10] [3,9] [4,8] [5,7] [6,6] - of 8 choices.
60 : "\" [9,12] : [5,8] [6,9] [7,10] [8,11] [9,12] - of 6 choices.
61 : "-" [4,12] : [4,9] [4,10] [4,11] [4,12] [4,13] - of 5 choices.
62 : "-" [5,0] : [5,0] [5,1] [5,2] [5,3] [5,4] - of 5 choices.
63 : "|" [1,9] : [1,9] [2,9] [3,9] [4,9] [5,9] - of 4 choices.
64 : "|" [6,2] : [4,2] [5,2] [6,2] [7,2] [8,2] - of 4 choices.
65 : "\" [5,13] : [1,9] [2,10] [3,11] [4,12] [5,13] - of 4 choices.
66 : "/" [8,-1] : [4,3] [5,2] [6,1] [7,0] [8,-1] - of 2 choices.
67 : "/" [8,0] : [4,4] [5,3] [6,2] [7,1] [8,0] - of 1 choices.
68 : "|" [6,0] : [5,0] [6,0] [7,0] [8,0] [9,0] - of 2 choices.
69 : "-" [8,-2] : [8,-2] [8,-1] [8,0] [8,1] [8,2] - of 3 choices.
70 : "\" [9,3] : [6,0] [7,1] [8,2] [9,3] [10,4] - of 2 choices.
71 : "/" [10,2] : [6,6] [7,5] [8,4] [9,3] [10,2] - of 3 choices.
72 : "|" [10,3] : [7,3] [8,3] [9,3] [10,3] [11,3] - of 2 choices.
73 : "-" [6,-1] : [6,-1] [6,0] [6,1] [6,2] [6,3] - of 2 choices.
74 : "\" [9,2] : [6,-1] [7,0] [8,1] [9,2] [10,3] - of 2 choices.
75 : "/" [11,2] : [7,6] [8,5] [9,4] [10,3] [11,2] - of 2 choices.
76 : "-" [9,1] : [9,0] [9,1] [9,2] [9,3] [9,4] - of 2 choices.
77 : "/" [10,0] : [6,4] [7,3] [8,2] [9,1] [10,0] - of 4 choices.
78 : "|" [12,2] : [8,2] [9,2] [10,2] [11,2] [12,2] - of 4 choices.
79 : "-" [10,1] : [10,0] [10,1] [10,2] [10,3] [10,4] - of 3 choices.
80 : "\" [12,4] : [8,0] [9,1] [10,2] [11,3] [12,4] - of 4 choices.
81 : "\" [7,-2] : [7,-2] [8,-1] [9,0] [10,1] [11,2] - of 2 choices.
82 : "/" [8,-3] : [4,1] [5,0] [6,-1] [7,-2] [8,-3] - of 2 choices.


Shortest (0) Game(s):
Morphion Solitare Grid (move=81):


Total run time = 32349320 ms
12345678901234567
&allocated (Total,static,string,block) : 1152124804 0 144396168 1007728636
1 .................
&collections (Total,static,string,block) : 4623 0 0 4623
2 ...........+.....
&regions ( - , - ,string,block) : - 0 41859440 41859440
3 ..........+......
&storage ( - , - ,string,block) : - 0 37784 13290772</pre>
4 .........+++++...
5 .....+..++++++...
6 .....+****++++...
7 .....+*++*+++++..
8 .....+*++*+++++..
9 ...****++****+.+.
10 ...*++++++++*+...
11 ...*++++++++*....
12 ..+****++****....
13 .+.+++*++*++.+...
14 ....++*++*+......
15 ...+.+****+......
16 .....+++++.......
17 .................
12345678901234567


== Other Interesting Results ==
#
One of things I did to get a feel for the game and perhaps get some insight in to strategies was to replay published record games. These universally look smoother and more organized than even the best random games. Somewhere it occurred to me to truncate these record games and play forward randomly to see what happened. I probably expected them to go off the rails fairly quickly. And while that happens, I was surprised how close these could get to the original record. This lead to my earlier observation that the seeds of success are set very early in morpion. It may also be due to the number of possible moves falling off more quickly than I might expect.
# Game Record for Morphion 5T game of 81 moves
* Using Bruneau's 170 game truncated at 112 moves the program produced the following:
# Reference: Games/81-L0(555)_20120208-090217.txt
<pre>Longest game 168 after 175790 simulations &random=1821730183.
# &random: 1350476946
Longest game 169 after 279675 simulations &random=873864083.
# Syntax for recorded games:
Longest game 170 after 1073380 simulations &random=2014543635.
# 1. whitespace and comments (everything past #) are ignored
Longest game 170 after 1086106 simulations &random=1319746023.
# 2. moves may be ; or newline separated
# 3. moves are formatted (using BNF style variables like <x>):
# <movenumber>:<row>,<col>[^<drow>,<dcol>]
# The optional drow/dcol values provide an additional point
# used to disambiguate moves. The point drow/dcol must be
# chosen at one end of the line.
# Moves are applied in sorted order.
# 4. all row/col coordinates are relative to the 1,1 origin
# located at the intersection of the lines containing the
# top and left most edges of the cross.
#
1:5,7^1,7;2:10,3^10,7;3:8,3^6,1;4:1,3^1,7;5:4,5^4,1;6:5,4^1,4;7:7,6^7,10;
8:8,1^4,1;9:11,7^7,7;10:11,4^7,4;11:6,3^4,5;12:7,5^7,2;13:6,5^4,3;14:3,10^7,10;
15:9,3^6,3;16:9,6^7,8;17:4,11^4,7;18:9,5^7,3;19:9,2^9,6;20:8,5^6,5;21:8,8^6,10;
22:7,0^11,4;23:5,2^9,6;24:8,2^8,1;25:6,7^10,3;26:8,-1^4,3;27:10,1^6,5;28:3,3^1,5;
29:11,5^7,1;30:6,6^5,7;31:5,5^4,4;32:5,3^5,1;33:6,2^4,2;34:3,5^7,1;35:8,6^6,6;
36:6,4^6,2;37:5,6^3,4;38:11,3^7,7;39:3,6^3,3;40:2,3^6,3;41:4,6^8,2;42:6,8^3,5;
43:2,5^6,5;44:0,7^4,3;45:6,9^6,6;46:2,6^1,6;47:10,8^6,4;48:3,8^1,6;49:5,9^2,6;
50:3,9^7,9;51:2,8^2,4;52:5,8^4,8;53:8,9^8,5;54:0,6^4,10;55:2,9^6,5;56:0,3^4,7;
57:11,6^11,3;58:9,8^7,10;59:3,11^3,7;60:8,11^4,7;61:2,12^6,8;62:2,11^6,7;
63:2,10^2,8;64:5,11^5,7;65:1,11^5,7;66:1,8^0,7;67:0,9^4,5;68:3,12^7,8;69:0,11^4,11;
70:0,8^4,8;71:-1,9^3,5;72:1,9^-1,9;73:0,10^0,6;74:-1,11^3,7;75:-1,7^3,11;
76:-2,8^2,12;77:1,10^1,7;78:4,13^0,9;79:-3,9^1,5;80:-1,10^3,10;81:-1,8^-1,7;


Move Log
1 : "|" [5,7] : [1,7] [2,7] [3,7] [4,7] [5,7] - of 4 choices.
2 : "-" [10,3] : [10,3] [10,4] [10,5] [10,6] [10,7] - of 27 choices.
3 : "\" [8,3] : [6,1] [7,2] [8,3] [9,4] [10,5] - of 25 choices.
4 : "-" [1,3] : [1,3] [1,4] [1,5] [1,6] [1,7] - of 24 choices.
5 : "-" [4,5] : [4,1] [4,2] [4,3] [4,4] [4,5] - of 22 choices.
6 : "|" [5,4] : [1,4] [2,4] [3,4] [4,4] [5,4] - of 21 choices.
7 : "-" [7,6] : [7,6] [7,7] [7,8] [7,9] [7,10] - of 20 choices.
8 : "|" [8,1] : [4,1] [5,1] [6,1] [7,1] [8,1] - of 20 choices.
9 : "|" [11,7] : [7,7] [8,7] [9,7] [10,7] [11,7] - of 19 choices.
10 : "|" [11,4] : [7,4] [8,4] [9,4] [10,4] [11,4] - of 17 choices.
11 : "/" [6,3] : [4,5] [5,4] [6,3] [7,2] [8,1] - of 15 choices.
12 : "-" [7,5] : [7,2] [7,3] [7,4] [7,5] [7,6] - of 16 choices.
13 : "\" [6,5] : [4,3] [5,4] [6,5] [7,6] [8,7] - of 14 choices.
14 : "|" [3,10] : [3,10] [4,10] [5,10] [6,10] [7,10] - of 14 choices.
15 : "|" [9,3] : [6,3] [7,3] [8,3] [9,3] [10,3] - of 12 choices.
16 : "/" [9,6] : [7,8] [8,7] [9,6] [10,5] [11,4] - of 11 choices.
17 : "-" [4,11] : [4,7] [4,8] [4,9] [4,10] [4,11] - of 12 choices.
18 : "\" [9,5] : [7,3] [8,4] [9,5] [10,6] [11,7] - of 9 choices.
19 : "-" [9,2] : [9,2] [9,3] [9,4] [9,5] [9,6] - of 10 choices.
20 : "|" [8,5] : [6,5] [7,5] [8,5] [9,5] [10,5] - of 12 choices.
21 : "/" [8,8] : [6,10] [7,9] [8,8] [9,7] [10,6] - of 17 choices.
22 : "\" [7,0] : [7,0] [8,1] [9,2] [10,3] [11,4] - of 17 choices.
23 : "\" [5,2] : [5,2] [6,3] [7,4] [8,5] [9,6] - of 16 choices.
24 : "-" [8,2] : [8,1] [8,2] [8,3] [8,4] [8,5] - of 15 choices.
25 : "/" [6,7] : [6,7] [7,6] [8,5] [9,4] [10,3] - of 16 choices.
26 : "/" [8,-1] : [4,3] [5,2] [6,1] [7,0] [8,-1] - of 15 choices.
27 : "/" [10,1] : [6,5] [7,4] [8,3] [9,2] [10,1] - of 12 choices.
28 : "/" [3,3] : [1,5] [2,4] [3,3] [4,2] [5,1] - of 9 choices.
29 : "\" [11,5] : [7,1] [8,2] [9,3] [10,4] [11,5] - of 8 choices.
30 : "/" [6,6] : [5,7] [6,6] [7,5] [8,4] [9,3] - of 6 choices.
31 : "\" [5,5] : [4,4] [5,5] [6,6] [7,7] [8,8] - of 8 choices.
32 : "-" [5,3] : [5,1] [5,2] [5,3] [5,4] [5,5] - of 7 choices.
33 : "|" [6,2] : [4,2] [5,2] [6,2] [7,2] [8,2] - of 8 choices.
34 : "/" [3,5] : [3,5] [4,4] [5,3] [6,2] [7,1] - of 10 choices.
35 : "|" [8,6] : [6,6] [7,6] [8,6] [9,6] [10,6] - of 12 choices.
36 : "-" [6,4] : [6,2] [6,3] [6,4] [6,5] [6,6] - of 16 choices.
37 : "\" [5,6] : [3,4] [4,5] [5,6] [6,7] [7,8] - of 16 choices.
38 : "/" [11,3] : [7,7] [8,6] [9,5] [10,4] [11,3] - of 15 choices.
39 : "-" [3,6] : [3,3] [3,4] [3,5] [3,6] [3,7] - of 14 choices.
40 : "|" [2,3] : [2,3] [3,3] [4,3] [5,3] [6,3] - of 13 choices.
41 : "/" [4,6] : [4,6] [5,5] [6,4] [7,3] [8,2] - of 11 choices.
42 : "\" [6,8] : [3,5] [4,6] [5,7] [6,8] [7,9] - of 12 choices.
43 : "|" [2,5] : [2,5] [3,5] [4,5] [5,5] [6,5] - of 11 choices.
44 : "/" [0,7] : [0,7] [1,6] [2,5] [3,4] [4,3] - of 13 choices.
45 : "-" [6,9] : [6,6] [6,7] [6,8] [6,9] [6,10] - of 12 choices.
46 : "|" [2,6] : [1,6] [2,6] [3,6] [4,6] [5,6] - of 14 choices.
47 : "\" [10,8] : [6,4] [7,5] [8,6] [9,7] [10,8] - of 16 choices.
48 : "\" [3,8] : [1,6] [2,7] [3,8] [4,9] [5,10] - of 15 choices.
49 : "\" [5,9] : [2,6] [3,7] [4,8] [5,9] [6,10] - of 16 choices.
50 : "|" [3,9] : [3,9] [4,9] [5,9] [6,9] [7,9] - of 18 choices.
51 : "-" [2,8] : [2,4] [2,5] [2,6] [2,7] [2,8] - of 17 choices.
52 : "|" [5,8] : [4,8] [5,8] [6,8] [7,8] [8,8] - of 18 choices.
53 : "-" [8,9] : [8,5] [8,6] [8,7] [8,8] [8,9] - of 12 choices.
54 : "\" [0,6] : [0,6] [1,7] [2,8] [3,9] [4,10] - of 11 choices.
55 : "/" [2,9] : [2,9] [3,8] [4,7] [5,6] [6,5] - of 9 choices.
56 : "\" [0,3] : [0,3] [1,4] [2,5] [3,6] [4,7] - of 9 choices.
57 : "-" [11,6] : [11,3] [11,4] [11,5] [11,6] [11,7] - of 8 choices.
58 : "/" [9,8] : [7,10] [8,9] [9,8] [10,7] [11,6] - of 8 choices.
59 : "-" [3,11] : [3,7] [3,8] [3,9] [3,10] [3,11] - of 7 choices.
60 : "\" [8,11] : [4,7] [5,8] [6,9] [7,10] [8,11] - of 6 choices.
61 : "/" [2,12] : [2,12] [3,11] [4,10] [5,9] [6,8] - of 5 choices.
62 : "/" [2,11] : [2,11] [3,10] [4,9] [5,8] [6,7] - of 4 choices.
63 : "-" [2,10] : [2,8] [2,9] [2,10] [2,11] [2,12] - of 4 choices.
64 : "-" [5,11] : [5,7] [5,8] [5,9] [5,10] [5,11] - of 4 choices.
65 : "/" [1,11] : [1,11] [2,10] [3,9] [4,8] [5,7] - of 5 choices.
66 : "\" [1,8] : [0,7] [1,8] [2,9] [3,10] [4,11] - of 4 choices.
67 : "/" [0,9] : [0,9] [1,8] [2,7] [3,6] [4,5] - of 5 choices.
68 : "/" [3,12] : [3,12] [4,11] [5,10] [6,9] [7,8] - of 4 choices.
69 : "|" [0,11] : [0,11] [1,11] [2,11] [3,11] [4,11] - of 3 choices.
70 : "|" [0,8] : [0,8] [1,8] [2,8] [3,8] [4,8] - of 1 choices.
71 : "/" [-1,9] : [-1,9] [0,8] [1,7] [2,6] [3,5] - of 4 choices.
72 : "|" [1,9] : [-1,9] [0,9] [1,9] [2,9] [3,9] - of 4 choices.
73 : "-" [0,10] : [0,6] [0,7] [0,8] [0,9] [0,10] - of 7 choices.
74 : "/" [-1,11] : [-1,11] [0,10] [1,9] [2,8] [3,7] - of 6 choices.
75 : "\" [-1,7] : [-1,7] [0,8] [1,9] [2,10] [3,11] - of 5 choices.
76 : "\" [-2,8] : [-2,8] [-1,9] [0,10] [1,11] [2,12] - of 3 choices.
77 : "-" [1,10] : [1,7] [1,8] [1,9] [1,10] [1,11] - of 2 choices.
78 : "\" [4,13] : [0,9] [1,10] [2,11] [3,12] [4,13] - of 4 choices.
79 : "/" [-3,9] : [-3,9] [-2,8] [-1,7] [0,6] [1,5] - of 2 choices.
80 : "|" [-1,10] : [-1,10] [0,10] [1,10] [2,10] [3,10] - of 1 choices.
81 : "-" [-1,8] : [-1,7] [-1,8] [-1,9] [-1,10] [-1,11] - of 1 choices.


Results from Sample of 1091265 games of Morpion 5T/D:
Morphion Solitare Grid (move=79):
Shortest game was 122 moves.
Average game was 127.0640165312733 moves.
Longest game was 170 moves.
Average time/game is 77.48127814967033 ms.
&random is now 609048351.


Summary of Results every '*' = 6761 games
1234567890123456
...
1 ................
115 | ( 0)
2 .....+..........
120 | (324190) ***********************************************
3 .....++.........
125 | (540953) ********************************************************************************
4 .....+****......
130 | (193255) ****************************
5 .+++++*++*+.....
135 | ( 17723) **
6 ..++++*++*+++++.
140 | ( 13447) *
7 ..+****++****+..
145 | ( 1577)
8 .++*++++++++*+..
150 | ( 83)
9 ..+*++++++++*++.
155 | ( 0)
10 ...****++****+..
160 | ( 28)
11 ....++*++*++++..
165 | ( 7)
12 ....++*++*++++..
170 | ( 2) </pre>
13 ....++****+.....
: The two games of 170 are both different and not just transpositions. However the difference produced is in a single move.
14 ....++++++.+....
* Using Rosin's 177 move (A) grid, the program produced the following,
15 ..........+.....
:* from move 129:
16 ................
<pre>Longest game 145 after 1 simulations.
1234567890123456
Longest game 153 after 2 simulations.
Longest game 154 after 20 simulations.
Longest game 155 after 40 simulations.
Longest game 164 after 50 simulations.
Longest game 168 after 2203 simulations.


Results from Sample of 78393 games of Morpion 5T:
#
# Game Record for Morphion 5T game of 79 moves
Shortest game was 143 moves.
Average game was 147.2826145191535 moves.
# Reference: Games/79-L0(357)_20120208-090217.txt
Longest game was 168 moves.
# &random: 417754092
Average time/game is 115.6193155001084 ms.
# Syntax for recorded games:
# 1. whitespace and comments (everything past #) are ignored
# 2. moves may be ; or newline separated
# 3. moves are formatted (using BNF style variables like <x>):
# <movenumber>:<row>,<col>[^<drow>,<dcol>]
# The optional drow/dcol values provide an additional point
# used to disambiguate moves. The point drow/dcol must be
# chosen at one end of the line.
# Moves are applied in sorted order.
# 4. all row/col coordinates are relative to the 1,1 origin
# located at the intersection of the lines containing the
# top and left most edges of the cross.
#
1:5,7^1,7;2:7,11^7,7;3:10,8^10,4;4:7,5^7,1;5:3,3^1,5;6:3,8^1,6;7:8,8^6,10;
8:11,4^7,4;9:1,3^1,7;10:5,9^3,7;11:4,6^4,10;12:4,0^4,4;13:6,2^4,0;14:3,1^7,1;
15:3,5^1,3;16:5,4^1,4;17:9,6^7,8;18:11,7^7,7;19:8,3^6,1;20:3,10^7,10;21:3,6^3,4;
22:5,3^3,5;23:6,6^4,8;24:6,3^3,3;25:4,5^2,7;26:6,4^3,1;27:6,0^6,4;28:5,5^4,4;
29:8,2^4,6;30:9,3^6,0;31:8,5^6,3;32:9,5^9,3;33:12,8^8,4;34:8,6^8,3;35:6,5^5,5;
36:2,5^1,5;37:0,3^4,7;38:5,6^5,3;39:2,6^1,6;40:11,9^7,5;41:5,2^2,5;42:9,2^5,6;
43:3,2^7,2;44:6,7^3,4;45:6,8^6,4;46:7,6^3,2;47:11,3^7,7;48:9,8^6,8;49:3,0^3,4;
50:5,8^3,10;51:10,3^7,3;52:11,6^7,6;53:3,11^7,7;54:2,8^6,8;55:2,3^2,7;56:8,9^7,10;
57:6,9^4,9;58:11,2^7,6;59:5,0^1,4;60:11,5^11,3;61:-1,3^3,3;62:5,11^5,7;
63:2,0^6,0;64:3,9^1,7;65:3,12^3,8;66:4,11^3,12;67:8,11^4,7;68:5,-1^5,3;
69:8,10^8,7;70:2,-1^6,3;71:10,2^7,2;72:9,9^7,11;73:2,2^1,3;74:2,1^2,-1;
75:9,11^5,7;76:6,11^5,11;77:9,10^9,7;78:0,4^-1,3;79:6,12^6,8;


Summary of Results every '*' = 973 games
Move Log
...
1 : "|" [5,7] : [1,7] [2,7] [3,7] [4,7] [5,7] - of 4 choices.
120 | ( 0)
2 : "-" [7,11] : [7,7] [7,8] [7,9] [7,10] [7,11] - of 27 choices.
140 | ( 77901) ********************************************************************************
3 : "-" [10,8] : [10,4] [10,5] [10,6] [10,7] [10,8] - of 26 choices.
160 | ( 492)</pre>
4 : "-" [7,5] : [7,1] [7,2] [7,3] [7,4] [7,5] - of 24 choices.
:* from move 112:
5 : "/" [3,3] : [1,5] [2,4] [3,3] [4,2] [5,1] - of 23 choices.
<pre>Longest game 140 after 1 simulations.
6 : "\" [3,8] : [1,6] [2,7] [3,8] [4,9] [5,10] - of 22 choices.
Longest game 146 after 10 simulations.
7 : "/" [8,8] : [6,10] [7,9] [8,8] [9,7] [10,6] - of 21 choices.
Longest game 148 after 441 simulations.
8 : "|" [11,4] : [7,4] [8,4] [9,4] [10,4] [11,4] - of 20 choices.
Longest game 151 after 2029 simulations.
9 : "-" [1,3] : [1,3] [1,4] [1,5] [1,6] [1,7] - of 19 choices.
Longest game 153 after 7167 simulations.
10 : "\" [5,9] : [3,7] [4,8] [5,9] [6,10] [7,11] - of 17 choices.
Longest game 157 after 34601 simulations.
11 : "-" [4,6] : [4,6] [4,7] [4,8] [4,9] [4,10] - of 16 choices.
Longest game 168 after 41977 simulations.
12 : "-" [4,0] : [4,0] [4,1] [4,2] [4,3] [4,4] - of 16 choices.
13 : "\" [6,2] : [4,0] [5,1] [6,2] [7,3] [8,4] - of 14 choices.
14 : "|" [3,1] : [3,1] [4,1] [5,1] [6,1] [7,1] - of 13 choices.
15 : "\" [3,5] : [1,3] [2,4] [3,5] [4,6] [5,7] - of 11 choices.
16 : "|" [5,4] : [1,4] [2,4] [3,4] [4,4] [5,4] - of 14 choices.
17 : "/" [9,6] : [7,8] [8,7] [9,6] [10,5] [11,4] - of 12 choices.
18 : "|" [11,7] : [7,7] [8,7] [9,7] [10,7] [11,7] - of 11 choices.
19 : "\" [8,3] : [6,1] [7,2] [8,3] [9,4] [10,5] - of 8 choices.
20 : "|" [3,10] : [3,10] [4,10] [5,10] [6,10] [7,10] - of 7 choices.
21 : "-" [3,6] : [3,4] [3,5] [3,6] [3,7] [3,8] - of 5 choices.
22 : "/" [5,3] : [3,5] [4,4] [5,3] [6,2] [7,1] - of 2 choices.
23 : "/" [6,6] : [4,8] [5,7] [6,6] [7,5] [8,4] - of 5 choices.
24 : "|" [6,3] : [3,3] [4,3] [5,3] [6,3] [7,3] - of 6 choices.
25 : "/" [4,5] : [2,7] [3,6] [4,5] [5,4] [6,3] - of 6 choices.
26 : "\" [6,4] : [3,1] [4,2] [5,3] [6,4] [7,5] - of 4 choices.
27 : "-" [6,0] : [6,0] [6,1] [6,2] [6,3] [6,4] - of 7 choices.
28 : "\" [5,5] : [4,4] [5,5] [6,6] [7,7] [8,8] - of 4 choices.
29 : "/" [8,2] : [4,6] [5,5] [6,4] [7,3] [8,2] - of 7 choices.
30 : "\" [9,3] : [6,0] [7,1] [8,2] [9,3] [10,4] - of 7 choices.
31 : "\" [8,5] : [6,3] [7,4] [8,5] [9,6] [10,7] - of 7 choices.
32 : "-" [9,5] : [9,3] [9,4] [9,5] [9,6] [9,7] - of 11 choices.
33 : "\" [12,8] : [8,4] [9,5] [10,6] [11,7] [12,8] - of 14 choices.
34 : "-" [8,6] : [8,3] [8,4] [8,5] [8,6] [8,7] - of 13 choices.
35 : "|" [6,5] : [5,5] [6,5] [7,5] [8,5] [9,5] - of 15 choices.
36 : "|" [2,5] : [1,5] [2,5] [3,5] [4,5] [5,5] - of 13 choices.
37 : "\" [0,3] : [0,3] [1,4] [2,5] [3,6] [4,7] - of 17 choices.
38 : "-" [5,6] : [5,3] [5,4] [5,5] [5,6] [5,7] - of 15 choices.
39 : "|" [2,6] : [1,6] [2,6] [3,6] [4,6] [5,6] - of 19 choices.
40 : "\" [11,9] : [7,5] [8,6] [9,7] [10,8] [11,9] - of 17 choices.
41 : "/" [5,2] : [2,5] [3,4] [4,3] [5,2] [6,1] - of 16 choices.
42 : "/" [9,2] : [5,6] [6,5] [7,4] [8,3] [9,2] - of 14 choices.
43 : "|" [3,2] : [3,2] [4,2] [5,2] [6,2] [7,2] - of 12 choices.
44 : "\" [6,7] : [3,4] [4,5] [5,6] [6,7] [7,8] - of 13 choices.
45 : "-" [6,8] : [6,4] [6,5] [6,6] [6,7] [6,8] - of 13 choices.
46 : "\" [7,6] : [3,2] [4,3] [5,4] [6,5] [7,6] - of 13 choices.
47 : "/" [11,3] : [7,7] [8,6] [9,5] [10,4] [11,3] - of 13 choices.
48 : "|" [9,8] : [6,8] [7,8] [8,8] [9,8] [10,8] - of 13 choices.
49 : "-" [3,0] : [3,0] [3,1] [3,2] [3,3] [3,4] - of 10 choices.
50 : "/" [5,8] : [3,10] [4,9] [5,8] [6,7] [7,6] - of 10 choices.
51 : "|" [10,3] : [7,3] [8,3] [9,3] [10,3] [11,3] - of 8 choices.
52 : "|" [11,6] : [7,6] [8,6] [9,6] [10,6] [11,6] - of 8 choices.
53 : "/" [3,11] : [3,11] [4,10] [5,9] [6,8] [7,7] - of 9 choices.
54 : "|" [2,8] : [2,8] [3,8] [4,8] [5,8] [6,8] - of 8 choices.
55 : "-" [2,3] : [2,3] [2,4] [2,5] [2,6] [2,7] - of 7 choices.
56 : "/" [8,9] : [7,10] [8,9] [9,8] [10,7] [11,6] - of 8 choices.
57 : "|" [6,9] : [4,9] [5,9] [6,9] [7,9] [8,9] - of 8 choices.
58 : "/" [11,2] : [7,6] [8,5] [9,4] [10,3] [11,2] - of 8 choices.
59 : "/" [5,0] : [1,4] [2,3] [3,2] [4,1] [5,0] - of 9 choices.
60 : "-" [11,5] : [11,3] [11,4] [11,5] [11,6] [11,7] - of 10 choices.
61 : "|" [-1,3] : [-1,3] [0,3] [1,3] [2,3] [3,3] - of 8 choices.
62 : "-" [5,11] : [5,7] [5,8] [5,9] [5,10] [5,11] - of 8 choices.
63 : "|" [2,0] : [2,0] [3,0] [4,0] [5,0] [6,0] - of 8 choices.
64 : "\" [3,9] : [1,7] [2,8] [3,9] [4,10] [5,11] - of 6 choices.
65 : "-" [3,12] : [3,8] [3,9] [3,10] [3,11] [3,12] - of 6 choices.
66 : "/" [4,11] : [3,12] [4,11] [5,10] [6,9] [7,8] - of 6 choices.
67 : "\" [8,11] : [4,7] [5,8] [6,9] [7,10] [8,11] - of 6 choices.
68 : "-" [5,-1] : [5,-1] [5,0] [5,1] [5,2] [5,3] - of 7 choices.
69 : "-" [8,10] : [8,7] [8,8] [8,9] [8,10] [8,11] - of 7 choices.
70 : "\" [2,-1] : [2,-1] [3,0] [4,1] [5,2] [6,3] - of 8 choices.
71 : "|" [10,2] : [7,2] [8,2] [9,2] [10,2] [11,2] - of 7 choices.
72 : "/" [9,9] : [7,11] [8,10] [9,9] [10,8] [11,7] - of 6 choices.
73 : "/" [2,2] : [1,3] [2,2] [3,1] [4,0] [5,-1] - of 5 choices.
74 : "-" [2,1] : [2,-1] [2,0] [2,1] [2,2] [2,3] - of 5 choices.
75 : "\" [9,11] : [5,7] [6,8] [7,9] [8,10] [9,11] - of 4 choices.
76 : "|" [6,11] : [5,11] [6,11] [7,11] [8,11] [9,11] - of 5 choices.
77 : "-" [9,10] : [9,7] [9,8] [9,9] [9,10] [9,11] - of 3 choices.
78 : "\" [0,4] : [-1,3] [0,4] [1,5] [2,6] [3,7] - of 2 choices.
79 : "-" [6,12] : [6,8] [6,9] [6,10] [6,11] [6,12] - of 1 choices.


Results from Sample of 524157 games of Morpion 5T:
Shortest (0) Game(s):
Shortest game was 126 moves.

Average game was 136.6568528131838 moves.
Total run time = 6463131 ms
Longest game was 168 moves.
&allocated (Total,static,string,block) : -232605485 0 119365103 -351970588
Average time/game is 138.334270075569 ms.
&collections (Total,static,string,block) : 618 0 0 618
&regions ( - , - ,string,block) : - 0 41859440 41859440
&storage ( - , - ,string,block) : - 0 275127 21216628


Summary of Results every '*' = 5643 games
Finished.</pre>
...
Oops, notice the memory counters overflowed here (known bug).
100 | ( 0)
120 | (451482) ********************************************************************************
140 | ( 72673) ************
160 | ( 2)</pre>
: Unfortunately that earlier version of the program was not logging the random number seed used for these games nor was it recording games in a notation that I have a converter for (at this time).
The above were run under "Unicon Version 12.0. July 13, 2011" on Windows 7/x64.

Latest revision as of 18:49, 30 August 2022

This example goes beyond the task goal of playing a random game of Morpion solitaire. The code is divided into two sections (a) to support the basic task and (b) extensions. The program was designed as a first cut/test bed more for understanding and exploring the problem space rather than for high efficiency. The program is structured to allow its behaviour to be configured (see options and M_ vars). Some of the features and extensions include:

  • Playing single games or running multigame simulations to find and capture the best games
  • Producing re-loadable game records and the ability to replay a saved game from a given position forward
  • The ability to reproduce purely random games from the random number seed
  • Limiting the initial random moves to 1 of 4 cases (inside corners (1 type) x 8, outside corners (2 types) x 8, outside valley (1 type x 4)) not 1 of 28. In the case of this program these are all in the North East quadrant.
  • Plays both 5T (by default) and 5D variants
  • Plugable modules for functions including applying player strategy, position evaluation (fitness/heuristics)to facilitate quick experimentation, changing game read/write functions, etc., etc.

Using random play in many summary runs, the highest (5T) score achieved was 92. While it was fairly easy to push random games into the low 80's running simulations of as little as a few hundred games, going beyond the highest score without adding some smarts will require some very long runs and is unlikely to result in any significant progress. Observing multigame runs show that play advances fairly quickly before progress grinds to a virtual halt. Many runs peak in the first few hundred or thousand games.

The ability to replay recorded games provides a way to study the behaviour of the problem. Selecting successful random games, truncating them and running multigame simulations using a successful base shows that the seeds of success are sown early. It was possible to better the results of good random game scores (mid 80s) pushing the new scores up into the mid 90s. Unfortunately this technique when applied to Chris Rosin's 177 move record game did not produce any new records  :(. Surprisingly however, it was possible to produce games in the high 160's! using random play with a truncated (approx 60%) version of this game.

The observation that a game becomes bad or good fairly early suggests that finding an intelligent forward looking position fitness evaluator is going to be a challenge to say the least.

It's also possible to capture bad games. This might provide some kind of comparison against good games when considering fitness evaluators. Or not.

Internally, the grid is automatically expanded as needed whenever a cell is played on an outer edge. When this happens only the affected side is expanded. As a side effect the grid numbering will seem unusual. The game log shows all row/col coordinates relative to the 1,1 origin located at the intersection of the lines containing top and left most edges of the cross. A fixed grid might be more efficient. With the ability to detect an off page condition and save and replay the game later this could be easier to deal with. The issue of grid origin comes up all the time when working with the code and debugging output.

The program produces a crude ASCII art grid; however and more importantly it produces a game log that can be replayed in the Pentasol free player. This can be used to capture graphical grids as well as providing independent validation of the games.

Most of the Record Games have downloadable games in this notation. For example Chris Rosin's 178 move grid.

For more see: Morpion -h|-help

Basic Solution

The code need to solve the task requirements is found in this section. Just delete the $define below. The basic code will play a single random game, show an ASCII art grid, and produce a log in Pentasol compatible notation. On any given run your most likely to see a game with scores in the 20's or 60's.

Main and Core Game Play Procedures

link printf,strings,options

$define MORPVER "1.7g"                    # version 
$define EXTENDED 1                        # delete line for basic

procedure main(A)                         # Morphion 
$ifdef EXTENDED
   MorpionConf(A)                         # conf extended version
   if \M_ReplayFile then ReplayMorpion() 
   else if \M_Limit === 1 then ShowGame(SingleMorpion())
   else MultiMorphion(\M_Limit)
$else
   printf("--- Morpion Solitaire 5 (v%s) (single random game)---\n\n",MORPVER)  
   M_Strategy     := RandomPlayer
   M_Mvalid       := ValidMove5T          # can be changed to 5D
   M_WriteGame    := WriteMoveLogPS
   M_Output       := &output 
   ShowGame(SingleMorpion())
$endif
end

$define XEMPTY "."                        # symbols used in Grid
$define XINIT  "*"
$define XUSED  "+"
$define DHOR "-"                          # Directions for moves
$define DVER "|"
$define DRD  "\\"
$define DLD  "/"
$define DALL "-|/\\"

global  M_Strategy,M_Mvalid               # Pluggable procedures
global  M_Output                          # output files
global  M_WriteGame                       # for logger
 
record morpiongame(grid,score,            # the grid & score
                  log,history,            # move log and replayable history log
                  roff,coff,center,       # origin expansion offsets and center 
                  pool,                   # pool of avail moves
                  move,                   # selected move
                  count,                  # game number (multi-game)
                  rseed)                  # &random at start of random play  
record morpionmove(direction,move,line,roff,coff)  # move & line data   
record morpioncell(symbol,direction,row,col)       # a grid cell                
record MorpionGameRecord(ref,row,col,darow,dacol)  # record of game

procedure SingleMorpion(MG,N)                      #: Play a game silently                        
   /MG := SetupM5Grid()                            # start game with new grid?
   while MorphionMove(M_Strategy(MG)) do           # keep moving     
      if MG.score >=  \N then break                # unless truncated   
   return MG
end

procedure MorphionMove(MG)                         #: make a move   
   (M := MG.move).roff := MG.roff                  # copy offsets
   M.coff := MG.coff                  
   put(MG.history,M)                               # save history
   MG.score +:= 1                                  # and score
   \M_LogDetails(MG,M)                             # for analysis
   every x := !M.line do {                         # draw the line 
      g := MG.grid[x[1],x[2]]
      g.direction ||:= M.direction       
      /g.symbol := XUSED                           # remove / for all XUSED
      }
   return
end

procedure ScanGrid(MG)                             #: Scan all grid lines
   G := ExpandGrid(MG).grid                        # expand the grid if needed
   MG.pool := []                                   # candidate moves   
   every c := 1 & r := 1 to *G do                  # horizontal
      ScanGridLine(G,r,c,0,+1,DHOR,MG.pool)
   every r := 1 & c := 1 to *G[1] do               # vertical
      ScanGridLine(G,r,c,+1,0,DVER,MG.pool)   
   every ( c := 1 & r := 1 to *G-4 ) |
         ( c := 2 to *G[r := 1] ) do               # down & right
      ScanGridLine(G,r,c,+1,+1,DRD,MG.pool)  
   every ( r := 2 to *G-4 & c := *G[r] ) |
         ( c := 5 to *G[r := 1] ) do               # down & left
   ScanGridLine(G,r,c,+1,-1,DLD,MG.pool)  
   if MG.score = 0 & M_Strategy ~=== Replayer then {  # move 1 special case
      every put(pool1 := [], MG.pool[2|19|20|26])  # cor. o(2), i(1), val o(1) 
      MG.pool := pool1       
      }
   if *MG.pool > 0 then return MG                 
end      

procedure ScanGridLine(G,r0,c0,ri,ci,dir,pool)     #: scan 1 grid line (5T/D)
   local L5,M,r,c,x
   L5 := []                          
   r := r0 - ri & c := c0 -ci                      # one step back
   while put(L5,G[r +:= ri,c +:= ci]) do {          
      while *L5 > 5 do pop(L5)                     # too long ? 
      if *L5 < 5 then next                         # too short ?
      if M_Mvalid(L5,dir) then {                   # just right, but valid?
         put(pool,M := morpionmove(dir,,[]))       # add to pool of valid moves               
         every x := L5[i := 1 to *L5] do  {
            put(M.line,[x.row,x.col])
            if /x.symbol then M.move := i
            }
         }
      }
   return pool
end

procedure ValidMove5T(L,dir)                 #: Succeed if L has valid 5T move
   local i,j
   if *L ~= 5 then fail                      # wrong count
   every (i := 0) +:= (/(!L).symbol,1)  
   if i ~= 1 then fail                       # more than 1 avail space
   every (j := 0) +:= ( find(dir,(!L).direction), 1) 
   if j > 1 then fail                        # no overlap, =1 implies at an end
   return                                    # that's it!
end

procedure ValidMove5D(L,dir)                 #: Succeed if L has valid 5D move  #@@
   local i,j
   if *L ~= 5 then fail                      # wrong count
   every (i := 0) +:= (/(!L).symbol,1)  
   if i ~= 1 then fail                       # more than 1 avail space
   every (j := 0) +:= ( find(dir,(!L).direction), 1) 
   if j > 0 then fail                        # no overlap, =1 implies at an end
   return                                    # that's it!
end

procedure SetupM5Grid()                      #: construct 5T/D grid & cross 
   local G,r,c,s
   every !(G := list(10)) := list(10)                          # Grid
   every G[r := 1 to 10, c := 1 to 10] := morpioncell(,"",r,c) # Empties  
   every s := ![[1,4],[4,1],[4,7],[7,1],[7,7],[10,4]] do {     # Cross
      every r := s[1] & c := s[2] + (0 to 3) do
         G[r,c] := morpioncell(XINIT,"",r,c)    
      every r := s[2] + (0 to 3) & c := s[1] do         
         G[r,c] := morpioncell(XINIT,"",r,c)    
      }                                                     
   return morpiongame(G,0,[],[],0,0,1 + (*G-1)/2.)    # Create game
end

procedure ExpandGrid(MG)                     #: expand any touching sides  
   local r,c,rn,cn,C
   rn := *(G := MG.grid)                     # capture ...
   cn := *G[1]                               # ... entry dimensions
   if \(!G)[1].symbol then {                 # left edge  
      MG.coff +:= 1   
      every (cn | (!!G).col) +:= 1
      every push(G[r := 1 to rn],morpioncell(,"",r,1))  
      }   
   if \(!G)[cn].symbol then  {               # right edge
      cn +:= 1   
      every put(G[r := 1 to rn],morpioncell(,"",r,cn))  
      }
   if \(!G[1]).symbol then {                 # top edge
      MG.roff +:= 1
      every (rn | (!!G).row) +:= 1
      push(G,C := list(cn))
      every C[c := 1 to cn] := morpioncell(,"",1,c)     
      }   
   if \(!G[rn]).symbol then {                # bottom edge
      rn +:= 1
      put(G,C := list(cn)) 
      every C[c := 1 to cn] := morpioncell(,"",rn,c)  
      }
   return MG
end

procedure ShowGame(MG)                 #: show games
   if M_Output === &output then   
      every (\(PrintGrid|WriteMoveLog|M_PrintDetails))(MG) 
   else                                # header first to output, game saved
      every (\(WriteMoveLog|PrintGrid|M_PrintDetails))(MG)                 
end

procedure PrintGrid(MG)                      #: print the current Grid  
   G := MG.grid
   every (ruler := "   ") ||:= (1 to *G[1]) % 10     
   fprintf(M_Output,"\nMorphion Solitare Grid (move=%i):\n%s\n",MG.score,ruler)                                              
   every r :=  1 to *G do {
      fprintf(M_Output,"%s ",right(r%100,2))                                  
      every c := 1 to *(G[r]) do 
         fprintf(M_Output,"%s",\G[r,c].symbol | XEMPTY)    
      fprintf(M_Output,"\n")
      }   
   fprintf(M_Output,"%s\n",ruler)                                                
   return MG
end

procedure RandomPlayer(MG)                      #: Simulate random player   
   if &random := \M_Rseed then M_Rseed := &null # set seed if given only once 
   /MG.rseed := &random                         # seed for this game          
   if MG.move := ?ScanGrid(MG).pool then return MG
end

Game Logging

procedure WriteMoveLog(MG)                     #: write move log wrapper    
   if \M_GameSave then {
      savegame := sprintf("Games/%s/%i-L%i",M_GameType,*MG.history,M_Limit) 
      if M_Limit ~= 1 then 
         savegame ||:= sprintf("(%i)",MG.count)
      if \M_ReplayFile then {
         fn := map(M_ReplayFile,"/\\","__")
         fn ? (="Games/", savegame ||:= "-RF" || tab(find(".txt")))
         savegame ||:= "-RN" || (0 < \M_ReplayAfter) 
         }
      savegame ||:= sprintf("_%s-%s-F%s.txt",deletec(&date,'/'),deletec(&clock,':'),M_GameFmt)  
      M_GameSave  := savegame                                 
      fprintf(M_Output,WriteMoveLogHeader(MG))  # write header, game is saved
      f := open(savegame,"w") | stop("Unable to open ",savegame," for writing")
      fprintf(f,M_WriteGame(MG),M_Config)       # call desired writer for output/save
      close(f)
      }
   else
      fprintf(M_Output,M_WriteGame(MG))
end

procedure WriteMoveLogHeader(MG)              #: write common header comments
   return sprintf("#\n# Game Record for Morphion %s game of %i moves\n_
                  # Date: %s\n# Saved: %s\n# &random: %i\n_
                  # ReplayFile: %s (%s moves)\n#\n",
                  \M_GameType|"5T",MG.score,&date,\M_GameSave|"* none *",
                  MG.rseed,\M_ReplayFile|"* none *",\M_ReplayAfter|"* all *")
end

procedure WriteMoveLogPS(MG)                  #: write pentasol style move log
   l := WriteMoveLogHeader(MG)
   l ||:= sprintf("# Pentasol compatible format\n#\n#\n_
                  # Morpion Solitaire game\n#\n_
                  #     XXXX\n#     X  X\n#     X  X\n_
                  #  XXXR  XXXX\n#  X        X\n#  X        X\n_
                  #  XXXX  XXXX\n#     X  X\n#     X  X\n#     XXXX\n#\n_
                  # R = reference point\n_
                  # List of moves starts with reference point (col,row)\n_
                  # Lines are\n#    (col,row) <direction> <+/-centerdist>\n_
                  #    distance to center is left side or top edge\n#\n")           
   l ||:= sprintf("(%i,%i)\n",4+MG.coff,4+MG.roff)                   
   every l ||:= FormatMoveLogPS(MG,!MG.history)
   return l || "#"
end

procedure FormatMoveLogPS(MG,m)                 #: format a PS move
   d := if m.direction == "/" then m.move-3 else 3-m.move
   return sprintf("(%i,%i) %s %s%d\n",
      m.line[m.move,2]-m.coff+MG.coff,m.line[m.move,1]-m.roff+MG.roff,
      m.direction,(d < 0,"-")|(d = 0,"")|"+",abs(d))
end

Extended Framework

None of the code below is needed to satisfy the basic task. It supports the extended framework which includes, command line options, reading and replaying games from a saved file, running mass simulations to glean the best games, and some code for detailed analysis if I ever get around to experimenting with other than random strategy.

Interface, Parameters, Globals

$ifdef EXTENDED

#  --- Interface, Parameters, Additional Globals --- 

global  M_Eval                                  # Pluggable procedure
global  M_SrchWid,M_SrchDep                     # For strategy modules
global  M_LogDetails,M_PrintDetails             # Misc. 
global  M_CommandLine,M_Config,M_GameType,M_GameSave
global  M_Limit,M_StatUpd,M_BestL,M_WorstL      # Multi-game simulation options
global  M_ReplayFile,M_ReplayAfter,M_Rseed      # For game replay
global  M_ReadGame,M_GameFmt                    # Game formats to use 
global  M_ChartW,M_ChartG                       # histogram 

# --- Beginning of Non-core (Extended) code ---

procedure MorpionConf(A)                           # Configure the Solver
   M_CommandLine := copy(A)                        # preserve
   os := "-Q! -L+ -limit+ -V: -variant: -seed+ "                        
   os ||:= "-R! -replay! -RF: -RN+ -save! -RW: -RR: "   
   os ||:= "-histwidth+ -histgroup+ -HW+ -HG+ "               
   os ||:= "-UN+ -SW+ -SD+ -A: -E+ -B+ -W+"
   os ||:= "-details! "
   opt := options(A,os,Usage)                      # -<anything else> gets help
   M_Limit     := ( 0 <= integer(\opt["limit"|"L"])) | 1  
   M_Rseed := \opt["seed"]   
   M_Mvalid := case opt["V"|"variant"] of {
      "5D"     :  (M_GameType := "5D", ValidMove5D)
      default  :  (M_GameType := "5T", ValidMove5T)   # also 5T  
      }                                           
   M_ReadGame  := case map(\opt["RR"]) | &null of { 
      default  : (M_GameFmt := "ps", ReadMoveLogPS)
      "0"      : (M_GameFmt := "0",  ReadMoveLogOrig) # deprecated
      }  
   M_WriteGame := case map(\opt["RW"]) | &null  of { 
      default  : (M_GameFmt := "ps", WriteMoveLogPS)
      "0"      : (M_GameFmt := "0",  WriteMoveLogOrig) # deprecated
      }  
   M_Strategy := case opt["A"] of {                    
      "A1"     : (def_un := 50,  PlayerA1) 
      default  : (def_un := 500, RandomPlayer)
      }       
   M_Eval := case opt["E"] of { 
      "1"      :  Score1         # test
      default  :  &null          # also "0"      
      }
   M_ChartW  := (40 <= \opt["histwidth"|"HW"]) | 80
   M_ChartG  := \opt["histgroup"|"HG"]         | 5
   M_LogDetails := if \opt["details"] then LogDetails else 1
   M_PrintDetails := if \opt["details"] then PrintDetails else 1  
   M_StatUpd      := (0 < \opt["UN"]) | def_un   
   M_BestL        := (0 < \opt["B"])  | 5  
   M_WorstL       := (0 < \opt["W"])  | 0    
   M_SrchWid      := (0 < \opt["SW"]) | 5   
   M_SrchDep      := (0 < \opt["SD"]) | 5    
   if \opt["R"|"replay"] then {
      M_ReplayFile   := \opt["RF"]       | "Games/5T/177-5T-rosin.txt" 
      M_ReplayAfter  := (0 < \opt["RN"]) | &null
      }
   else M_ReplayFile   := &null     
   if \(M_GameSave  := opt["save"]) then {
      fn := sprintf("Runs/%s-L%i-",M_GameType,M_Limit)
      fn ||:= sprintf("RF%s-",map(\M_ReplayFile,"/\\","__"))
      fn ||:= sprintf("RN%i-",\M_ReplayAfter)
      fn ||:= sprintf("%s-%s.txt",deletec(&date,'/'),deletec(&clock,':'))
      M_Output := open(fn,"w") 
      }
   /M_Output := &output 
   
   c := sprintf("# --- Morpion Solitaire 5 (v%s) ---\n#\n",MORPVER)  
   c ||:= "# Command line options :"
   every c ||:= " " || !A
   c ||:=         "\n# Summary of Morpion Configuration:\n"  
   c ||:= sprintf("#   Variant (5T/D) move validation      = %i\n",M_Mvalid)    
   c ||:= sprintf("#   Games to play                       = %s\n",(
                   0 < M_Limit) | "* unlimited *")     
   c ||:=         "#   Multi-game options:\n"
   c ||:= sprintf("#   - Status Updates                    = %i\n",M_StatUpd) 
   c ||:= sprintf("#   - Keep best                         = %i\n",M_BestL)   
   c ||:= sprintf("#   - Keep worst                        = %i\n",M_WorstL)  
   c ||:= sprintf("#   - Histogram width                   = %i\n",M_ChartW)  
   c ||:= sprintf("#   - Histogram grouping                = %i\n",M_ChartG)  
   c ||:= sprintf("#   Games will be saved                 = %s\n",
                  (\M_GameSave, "Yes") | "* No *")
   c ||:= sprintf("#   - Format for game file (write)      = %i\n",\M_WriteGame)    
   c ||:=         "#   Replaying\n"
   c ||:= sprintf("#   - Format for game file (read)       = %i\n",\M_ReadGame)   
   c ||:= sprintf("#   - Game file to be replayed          = %s\n",
                  \M_ReplayFile | "* None *")    
   c ||:= sprintf("#   - Moves to replay                   = %i\n",
                  0 ~= \M_ReplayAfter)  
   c ||:= sprintf("#   Player Strategy                     = %i\n",M_Strategy)  
   c ||:= sprintf("#   - Seed for &random                  = %i\n",\M_Rseed)     
   c ||:= sprintf("#   - Position Fitness Evaluator        = %s\n",
                  image(\M_Eval) | "* None *")     
   c ||:= sprintf("#   - Search Width (strategy dependant) = %i\n",M_SrchWid)   
   c ||:= sprintf("#   - Search Depth (strategy dependant) = %i\n",M_SrchDep)  
   c ||:= sprintf("#   Log Details for analysis            = %s\n",
                  if M_LogDetails === 1 then "No" else "Yes")  
   c ||:=         "#\n"
   M_Config := c
   if \opt["Q"] then stop(M_Config,"-Q Stops run after processing options")
   else fprintf(M_Output,M_Config)
   /M_Eval := Scorefail          
end

procedure Usage()
   fprintf(&errout,"_
      Morphion [options]  Plays the 5T/D variant of Morphion Solitaire\n_ 
      Arguments : ")  
   every fprintf(&errout," %s",!M_CommandLine)
   fprintf(&errout,"_                                                         
      Morphion [options]  Plays the 5T/D variant of Morphion Solitaire\n_ 
      Where options are:\n_                           
      \t-A\tchoose player strategy approach (default=random, future)\n_
      \t-E\tSpecifiy an position fitness evaluation function (default none, future)\n_       
      \t-SW\tSearch width (strategy dependent, future)\n_
      \t-SD\tSearch depth (strategy dependent, future)\n_    
      \t-V|-variant\t5D or 5T (default)\n_
      \t-R|-replay\treplay\n_
      \t-RF\tfile containing game record to be replayed\n_
      \t-RN\tnumber of moves to replay (0=all)\n_
      \t-RR\tgame recording format to read (ps=pentasol(default), 0=original)\n_
      \t-RW\tgame recording format to write ps=pentasol(default), 0=original)\n_
      \t-save\tsave the best (-B) and worst (-W) games\n_
      \t-seed\tstart the seed of the random number at this value\n_  
      \t-L|-limit\tgames to play (if 0 or less then play until any of 'XxQq' is pressed\n_
      \t\tnote for larger n this benefits from larger BLKSIZE, STRSIZE environment variables\n_   
      \t-HW|-histwidth\twidth (cols) of histogram of scores\n_
      \t-HG|-histgroup\tsize of groups (buckets) for histogram\n_
      \t-UN\tGive status update notifications every n simulations\n_  
      \t-B\tKeep best n games of unique length (default 5)\n_        
      \t-W\tKeep worst n games of unique length (default 3)\n_
      \t-details\tLog game details for analysis\n_
      \t-Q\t(debugging) terminates after options processing\n_
      \t-?|-h|-help\tthis help text")  
   stop()
end

Multigame Simulation and Monitoring Support

procedure MultiMorphion(N,MG)                      #: Simulate N games using MG
      etime := -&time 
      scores := table(n := 0)   
      every bestL|worstL  := []        
      if N <= 0 then N := "unlimited"         
      repeat {
         if n >= numeric(N) then break else n +:= 1  
         mg := SingleMorpion(deepcopy(\MG)|&null)  # play out game
         scores[mg.score] +:= 1                    # count score
         mg.count := n                             # game number
         if short := ( /short | short.score >= mg.score, mg) then {   
            push(worstL,short)                     # keep worst 
            if *worstL > M_WorstL then pull(worstL)
            }
         if ( /long | long.score <= mg.score) then {                 
            bestcnt := if (\long).score = mg.score then bestcnt + 1 else 1 
            long := mg                                                 
            put(bestL,long)                        # keep best
            if *bestL > M_BestL then get(bestL)
            fprintf(M_Output,"Longest game %i after %i simulations &random=%i.\n",                
                   long.score,n,long.rseed)                                       
            fprintf(&errout,"\r%i of %s simulations, long=%i(%i) (%s %s)",                  
                            n,N,long.score,bestcnt,&date,&clock)                              
            }            
         if (n % M_StatUpd) = 0 then               # say we're alive & working
            fprintf(&errout,"\r%i of %s simulations, long=%i(%i) (%s %s)",       
                            n,N,long.score,bestcnt,&date,&clock)                  
         if kbhit() & getch() == !"QqXx" then      # exit if any q/x
            break fprintf(&errout,"\nExiting after %i simulations.\n",n)  
         }
      etime +:= &time   
      avg := 0.0
      short := key(scores) \ 1                  # 1 key only
      every i := key(scores) do {               # summarize stats
         short >:= i
         avg +:= i * scores[i]
         }       
      fprintf(M_Output,"\nResults from Sample of %i games of Morpion 5T/D:\n",n)
      fprintf(M_Output,"Shortest game was %i moves.\n",short)
      fprintf(M_Output,"Average game was %i moves.\n",avg /:= n)
      fprintf(M_Output,"Longest game was %i moves.\n",long.score)
      fprintf(M_Output,"Average time/game is %i ms.\n",etime/real(n))
      fprintf(M_Output,"&random is now %i.\n",&random)                     
      GraphScores(scores)              # graph results
      fprintf(M_Output,"\nLongest (%i) Game(s):\n",M_BestL)
      every ShowGame(!reverse(bestL))  # show longest game(s) and log
      fprintf(M_Output,"\nShortest (%i) Game(s):\n",M_WorstL) 
      every ShowGame(!worstL)          # show longest game(s) and log     
      MemUsage()                       # diagnostic        
end

procedure GraphScores(S)               #: graph results
   chart := []
   every s := key(S) do {              # by score 
      n := s/M_ChartG+1                  # chunks of ...
      until chart[n] do put(chart,0)   # grow chart to need
      chart[n] +:= S[s]
      }
   s := (1 < max!chart/M_ChartW | 1)     # scale 
   fprintf(M_Output,"\nSummary of Results every '*' = %d games\n",s)
   every n := 1 to *chart do 
      fprintf(M_Output,"%3d | (%6d) %s\n",(n-1)*M_ChartG,chart[n],repl("*",chart[n]/s))
end

procedure MemUsage()                   #: monitor usage
      fprintf(M_Output,"\nTotal run time = %i ms\n",&time)
      fprintf(M_Output,"&allocated   (Total,static,string,block) : ")
      every fprintf(M_Output," %i",&allocated) ;  fprintf(M_Output,"\n")      
      fprintf(M_Output,"&collections (Total,static,string,block) : ")
      every fprintf(M_Output," %i",&collections) ;  fprintf(M_Output,"\n")
      fprintf(M_Output,"&regions     ( -   , -    ,string,block) : ")
      every fprintf(M_Output," %s","-"|&regions) ;  fprintf(M_Output,"\n")          
      fprintf(M_Output,"&storage     ( -   , -    ,string,block) : ")
      every fprintf(M_Output," %s","-"|&storage) ;  fprintf(M_Output,"\n\n")  
      fprintf(M_Output,"Icon/Unicon version %s\n",&version)       
end

Game Replayer

procedure ReplayMorpion()                    #: Handle recorded games
   Replayer(M := ReadMoveLog(M_ReplayFile))  # read game and save data 
   M_Strategy := Replayer  
   if /M_ReplayAfter | (M_ReplayAfter > *M) then {
      fprintf(M_Output,"Single game replay\n")
      ShowGame(SingleMorpion())
      }
   else {                                       # truncation replay  
      MG := SingleMorpion(,M_ReplayAfter)       # play shortened game
      M_Strategy := RandomPlayer     
      if M_Limit === 1 then 
         ShowGame(SingleMorpion(MG))            # single game
      else
         MultiMorphion(M_Limit,MG)              # simulate many games from here
      }
   return
end

procedure Replayer(MG)                    #: feed replayed moves from list/game
static ML,radj,cadj
   if type(MG[1]) == "MorpionGameRecord" then return ML := MG # setup list
   if not ScanGrid(MG) then fail          # out of moves ?
   x := get(ML) | fail                    # get next move
   if x.ref = 0 then x := get(ML) | fail  # skip move 0 if any
   xr := x.row + MG.roff                  # adjust move for grid expansion
   xc := x.col + MG.coff      
   dr := \x.darow + MG.roff               # adjust end for grid expansion
   dc := \x.dacol + MG.coff 
   pool := []
   every m := !MG.pool do {               # find possible moves here
      mr := m.line[m.move,1]  
      mc := m.line[m.move,2]
      if xr=mr & xc=mc then    
         if \dr & \dc then {              # info to disambiguate?
            every p := (m.line)[1|5] do   # try endpoints
               if p[1] = dr & p[2] = dc then 
                  put(pool,m)             # save matching move
            }
         else 
            put(pool,m)                   # save matching move(s)
      } 
   if *pool = 1 then                      # unique move?
      return ( MG.move := pool[1], MG)    # set unique move and return MG  
   else {                                 # we have a problem
      ShowGame(MG)
      fprintf(M_Output,"Problem encountered replaying game at move #%i, %i choices.\n",MG.score,*pool)    
      every m := !pool do 
         fprintf(M_Output,"   %s\n",FormatMoveLogPS(MG,m))        
      &dump := 0
      stop()
      }         
end

Game Reader

procedure ReadMoveLog(MG)                     #: read move log wrapper  
   fprintf(M_Output,"Reading recorded game from: %s\n",M_ReplayFile)
   f :=  open(M_ReplayFile ,"r") | 
         stop("Unable to open file ",M_ReplayFile ," for read.")
   R := []
   while b := trim(read(f)) do {
      if b ? ="$end" then break               # allow pre-mature end of file
      b ?:= tab(find("#")|0)                  # strip comments
      b := deletec(b,' \t')                   # strip whitespace
      if *b > 0 then put(R,b)                 # save move for reader
      }
   close(f)
   return M_ReadGame(R)                       # call reader, return move list
end

procedure ReadMoveLogPS(R)                    #: read pentasol style move log
static off
initial {                                     # precalc center offsets
   off := table()
   off[-2] := -4
   off[-1] := -3
   off[0]  := 2
   off[1]  := -1
   off[2]  := 4
   }
   M := []
   n := 0                                     # move number
   get(R) ? ( ="(", coff := integer(tab(many(&digits))) - 4, 
              =",", roff := integer(tab(many(&digits))) - 4, 
              =")", pos(0) )  |               # Reference Cell (c,r)
              stop(&output,"Syntax error in reference line.")              
   while b := get(R) do {                     # Line   (c,r) d o       
      b ? ( ="(", c := integer(tab(many(&digits))), 
            =",", r := integer(tab(many(&digits))), 
            =")", d := tab(any(DALL)),
            o := integer(=("-2"|"-1"|0|"+1"|"+2")) )  |
            stop(&output,"Syntax error in line above.")
      x := MorpionGameRecord()                # new move /  line
      x.ref := n +:= 1
      x.darow := x.row := r - roff
      x.dacol := x.col := c - coff
      case d of {                             # adjust based on direction
         DHOR : x.dacol +:= off[o]
         DVER : x.darow +:= off[o]
         DRD  : ( x.darow +:= off[o],  x.dacol +:= off[o])
         DLD  : ( x.darow -:= off[o],  x.dacol +:= off[o])
         }                                             
      put(M,x)
      }
   return M
end

Detailed Move Logging (for analysis)

procedure PrintDetails(MG)                      #: print the log
   fprintf(M_Output,"Detailed Move Log\n")
   every fprintf(M_Output,"%i : %s\n",i := 1 to *MG.log,MG.log[i])
end

procedure LogFormatMove(M,roff,coff)            #: format a log entry
   /M.roff := \roff | 0  
   /M.coff := \coff | 0 
   log := sprintf("\"%s\" [%i,%i] : ",M.direction,
                  M.line[M.move,1]-M.roff,M.line[M.move,2]-M.coff)  
   every x := !M.line do                           
      log ||:= sprintf("[%i,%i] ",x[1]-M.roff,x[2]-M.coff)    
   return log
end

procedure LogDetails(MG,M)                       #: Record details
   log := LogFormatMove(M)            
   log ||:= sprintf(" - of %i choices.",*MG.pool)  # append # choices  
   log ||:= sprintf(" Metric=%i",M_Eval(MG))       # append score (opt)
   put(MG.log,log)                                 # log the move
end

Strategy Support

# No useful examples at this time

procedure Scorefail(MG);end                     #: dummy M_Eval always fails
$endif

printf.icn provides formatting strings.icn provides deletec options.icn provides options processing

Other RosettaCode pages used Deepcopy

SampleOutput

Help

Morphion [options]  Plays the 5T/D variant of Morphion Solitaire
Arguments :  -helpMorphion [options]  Plays the 5T/D variant of Morphion Solitaire
Where options are:
	-A	choose player strategy approach (default=random, future)
	-E	Specifiy an position fitness evaluation function (default none, future)
	-SW	Search width (strategy dependent, future)
	-SD	Search depth (strategy dependent, future)
	-V|-variant	5D or 5T (default)
	-R|-replay	replay
	-RF	file containing game record to be replayed
	-RN	number of moves to replay (0=all)
	-RR	game recording format to read (ps=pentasol(default), 0=original)
	-RW	game recording format to write ps=pentasol(default), 0=original)
	-save	save the best (-B) and worst (-W) games
	-seed	start the seed of the random number at this value
	-L|-limit	games to play (if 0 or less then play until any of 'XxQq' is pressed
		note for larger n this benefits from larger BLKSIZE, STRSIZE environment variables
	-HW|-histwidth	width (cols) of histogram of scores
	-HG|-histgroup	size of groups (buckets) for histogram
	-UN	Give status update notifications every n simulations
	-B	Keep best n games of unique length (default 5)
	-W	Keep worst n games of unique length (default 3)
	-details	Log game details for analysis
	-Q	(debugging) terminates after options processing
	-?|-h|-help	this help text


Random Game Record

The following image was obtained by replaying the game in Pentasol and saving the picture. The original was generated by a multigame simulation and represents the best game generated by this game from the starting position.


This the game record used to produce the above image:

#
# Game Record for Morphion 5T game of 92 moves
# Date: 2012/02/18
# Saved: Games/5T/92-L1_20120218-163208-Fps.txt
# &random: 1617565851
# ReplayFile: * none * (* all * moves)
#
# Pentasol compatible format
#
#
# Morpion Solitaire game
#
#     XXXX
#     X  X
#     X  X
#  XXXR  XXXX
#  X        X
#  X        X
#  XXXX  XXXX
#     X  X
#     X  X
#     XXXX
#
# R = reference point
# List of moves starts with reference point (col,row)
# Lines are
#    (col,row) <direction> <+/-centerdist>
#    distance to center is left side or top edge
#
(9,7)
(12,8) | -2
(16,7) - -2
(9,8) | -2
(13,11) / 0
(9,9) | +2
(15,11) | -2
(13,13) - -2
(6,11) | -2
(16,10) - -2
(12,14) | -2
(8,4) - +2
(5,7) - +2
(13,6) \ 0
(10,12) \ 0
(14,8) \ 0
(14,12) / 0
(10,10) - -2
(11,9) / 0
(8,6) / 0
(10,8) \ +1
(8,11) \ 0
(11,7) / -1
(13,9) \ 0
(11,11) \ 0
(14,11) - -1
(14,9) | +1
(13,12) | -1
(12,9) - +1
(16,9) / -2
(17,6) / -2
(11,12) - +1
(16,6) / -2
(16,8) | 0
(11,10) | +1
(10,9) \ +1
(11,8) / 0
(13,8) - -2
(15,6) / -2
(13,5) | +2
(10,7) \ +2
(14,6) \ 0
(10,6) | +2
(16,11) \ -2
(11,6) - +2
(11,5) | +2
(8,8) / +2
(8,14) / +2
(8,9) | -1
(7,9) - +2
(4,6) \ +2
(5,12) / +2
(13,4) / -2
(10,5) - +1
(10,11) \ 0
(7,8) / +2
(7,6) | +2
(7,11) - +1
(10,14) | -2
(9,14) / +2
(7,3) \ +2
(17,8) - -2
(14,5) \ +1
(11,14) - -1
(8,12) \ 0
(5,8) - +2
(8,13) | -1
(14,4) | +2
(8,5) / -1
(7,14) / +2
(8,3) \ +2
(6,6) - +2
(5,5) \ +2
(8,2) | +2
(6,4) / 0
(9,3) \ +1
(7,5) / 0
(5,3) \ +2
(6,3) - +1
(6,5) - +1
(6,2) | +2
(7,4) \ +1
(7,2) | +2
(5,4) \ +2
(5,6) | 0
(9,2) / -2
(10,2) - -2
(4,5) \ +2
(10,3) | +1
(3,6) / +2
(4,4) - +2
(2,6) - +2
(3,5) / +1
#

Multigame Run

The multigame simulation of completely random play includes a histogram showing game scores clustering in the 20's and 60's. This result is similar to results obtained by Jean-Jacques Sibil (you will have to scroll down that page to find the reference) who has run nearly a billion such simulations achieving a high score of 102 as of 2010.

The following is a summary file:

# --- Morpion Solitaire 5 (v1.7e) ---
#
# Command line options :
# Summary of Morpion Configuration:
#   Variant (5T/D) move validation      = procedure ValidMove5T
#   Games to play                       = * unlimited *
#   Multi-game options:
#   - Status Updates                    = 500
#   - Keep best                         = 5
#   - Keep worst                        = 0
#   - Histogram width                   = 80
#   - Histogram grouping                = 5
#   Games will be saved                 = Yes
#   - Format for game file (write)      = procedure WriteMoveLogPS
#   Replaying
#   - Format for game file (read)       = procedure ReadMoveLogPS
#   - Game file to be replayed          = * None *
#   Player Strategy                     = procedure RandomPlayer
#   - Position Fitness Evaluator        = * None *
#   - Search Width (strategy dependant) = 5
#   - Search Depth (strategy dependant) = 5
#   Log Details for analysis            = No
#
Longest game 77 after 1 simulations &random=20122297.
Longest game 77 after 85 simulations &random=274082001.
Longest game 78 after 118 simulations &random=559240181.
Longest game 78 after 123 simulations &random=1682993637.
Longest game 81 after 292 simulations &random=826134037.
Longest game 84 after 1181 simulations &random=1936506737.
Longest game 86 after 4584 simulations &random=1266457499.
Longest game 86 after 44424 simulations &random=1725594333.
Longest game 86 after 47918 simulations &random=1686351259.
Longest game 86 after 50600 simulations &random=665807725.
Longest game 87 after 60841 simulations &random=152917603.
Longest game 87 after 74778 simulations &random=1037682795.
Longest game 88 after 173368 simulations &random=72059739.
Longest game 88 after 241134 simulations &random=2095899781.

Results from Sample of 242921 games of Morpion 5T/D:
Shortest game was 20 moves.
Average game was 53.65064774144681 moves.
Longest game was 88 moves.
Average time/game is 133.1678282239905 ms.
&random is now 452165683.

Summary of Results every '*' = 940 games
  0 | (     0) 
  5 | (     0) 
 10 | (     0) 
 15 | (     0) 
 20 | ( 38637) *****************************************
 25 | ( 13790) **************
 30 | (  6657) *******
 35 | (  2604) **
 40 | (  1306) *
 45 | (  1088) *
 50 | (  3448) ***
 55 | ( 22481) ***********************
 60 | ( 75207) ********************************************************************************
 65 | ( 61501) *****************************************************************
 70 | ( 13902) **************
 75 | (  1922) **
 80 | (   349) 
 85 | (    29) 

Longest (5) Game(s):
#
# Game Record for Morphion 5T game of 88 moves
# Date: 2012/02/18
# Saved: Games/5T/88-L0(241134)_20120218-083009-Fps.txt
# &random: 2095899781
# ReplayFile: * none * (* all * moves)
#

Morphion Solitare Grid (move=88):
   1234567890123456
 1 ................
 2 ....+.+.........
 3 .....+++........
 4 ..+++++++.......
 5 .+++++++++......
 6 ..++++****+.....
 7 .+++++*++*+.....
 8 ..++++*++*++....
 9 ...****++****+..
10 ...*++++++++*+..
11 .++*++++++++*++.
12 ...****++****+..
13 ....++*++*++++..
14 .....+*++*+++...
15 .....+****+.....
16 .......++.......
17 ........+.......
18 ................
   1234567890123456
#
# Game Record for Morphion 5T game of 88 moves
# Date: 2012/02/18
# Saved: Games/5T/88-L0(173368)_20120218-083009-Fps.txt
# &random: 72059739
# ReplayFile: * none * (* all * moves)
#

Morphion Solitare Grid (move=88):
   1234567890123456
 1 ................
 2 .......+........
 3 ......++...+....
 4 .....+****+.....
 5 ..++++*++*+.....
 6 ..++++*++*+++...
 7 ...****++****+..
 8 ...*++++++++*+..
 9 .++*++++++++*+..
10 ...****++****+..
11 ..++++*++*+++++.
12 .+...+*++*+++++.
13 .....+****++++..
14 ....+..+++++++..
15 .........+++++..
16 ..........+.....
17 ...........+....
18 ................
   1234567890123456
#
# Game Record for Morphion 5T game of 87 moves
# Date: 2012/02/18
# Saved: Games/5T/87-L0(74778)_20120218-083009-Fps.txt
# &random: 1037682795
# ReplayFile: * none * (* all * moves)
#

Morphion Solitare Grid (move=87):
   123456789012345
 1 ...............
 2 ....+.+........
 3 .....+.........
 4 ....+++++......
 5 ...++++++......
 6 .+++++****+....
 7 ..++++*++*+....
 8 .+++++*++*++...
 9 ..+****++****..
10 ..+*++++++++*+.
11 ...*++++++++*..
12 ..+****++****+.
13 ...+++*++*++++.
14 ..++++*++*++...
15 .....+****+.+..
16 ......+++++....
17 ........+......
18 ...............
   123456789012345
#
# Game Record for Morphion 5T game of 87 moves
# Date: 2012/02/18
# Saved: Games/5T/87-L0(60841)_20120218-083009-Fps.txt
# &random: 152917603
# ReplayFile: * none * (* all * moves)
#

Morphion Solitare Grid (move=87):
   123456789012345678
 1 ..................
 2 .........+.+..+...
 3 .....+...++++++...
 4 ......****+++++...
 5 .....+*++*+++++...
 6 ....++*++*+++++++.
 7 ..+****++****+.+..
 8 .++*++++++++*+....
 9 .++*++++++++*+....
10 ..+****++****+....
11 .+++++*++*++++....
12 ....++*++*++......
13 ....++****........
14 ....+++...........
15 ..................
   123456789012345678
#
# Game Record for Morphion 5T game of 86 moves
# Date: 2012/02/18
# Saved: Games/5T/86-L0(50600)_20120218-083009-Fps.txt
# &random: 665807725
# ReplayFile: * none * (* all * moves)
#

Morphion Solitare Grid (move=86):
   1234567890123456
 1 ................
 2 .....++..+++....
 3 .....+****+.....
 4 ....+.*++*+.....
 5 ...+.+*++*+..+..
 6 ..+****++****...
 7 ..+*++++++++*...
 8 ..+*++++++++*...
 9 ..+****++****++.
10 .+++++*++*+++++.
11 ....++*++*+++++.
12 .....+****+++++.
13 .....++++++++++.
14 ..........+++++.
15 ..........+.....
16 ................
   1234567890123456

Shortest (0) Game(s):

Total run time = 32349320 ms
&allocated   (Total,static,string,block) :  1152124804 0 144396168 1007728636
&collections (Total,static,string,block) :  4623 0 0 4623
&regions     ( -   , -    ,string,block) :  - 0 41859440 41859440
&storage     ( -   , -    ,string,block) :  - 0 37784 13290772

Other Interesting Results

One of things I did to get a feel for the game and perhaps get some insight in to strategies was to replay published record games. These universally look smoother and more organized than even the best random games. Somewhere it occurred to me to truncate these record games and play forward randomly to see what happened. I probably expected them to go off the rails fairly quickly. And while that happens, I was surprised how close these could get to the original record. This lead to my earlier observation that the seeds of success are set very early in morpion. It may also be due to the number of possible moves falling off more quickly than I might expect.

  • Using Bruneau's 170 game truncated at 112 moves the program produced the following:
Longest game 168 after 175790 simulations &random=1821730183.
Longest game 169 after 279675 simulations &random=873864083.
Longest game 170 after 1073380 simulations &random=2014543635.
Longest game 170 after 1086106 simulations &random=1319746023.


Results from Sample of 1091265 games of Morpion 5T/D:
Shortest game was 122 moves.
Average game was 127.0640165312733 moves.
Longest game was 170 moves.
Average time/game is 77.48127814967033 ms.
&random is now 609048351.

Summary of Results every '*' = 6761 games
...
115 | (     0) 
120 | (324190) ***********************************************
125 | (540953) ********************************************************************************
130 | (193255) ****************************
135 | ( 17723) **
140 | ( 13447) *
145 | (  1577) 
150 | (    83) 
155 | (     0) 
160 | (    28) 
165 | (     7) 
170 | (     2) 
The two games of 170 are both different and not just transpositions. However the difference produced is in a single move.
  • Using Rosin's 177 move (A) grid, the program produced the following,
  • from move 129:
Longest game 145 after 1 simulations.
Longest game 153 after 2 simulations.
Longest game 154 after 20 simulations.
Longest game 155 after 40 simulations.
Longest game 164 after 50 simulations.
Longest game 168 after 2203 simulations.

Results from Sample of 78393 games of Morpion 5T:
Shortest game was 143 moves.
Average game was 147.2826145191535 moves.
Longest game was 168 moves.
Average time/game is 115.6193155001084 ms.

Summary of Results every '*' = 973 games
... 
120 | (     0) 
140 | ( 77901) ********************************************************************************
160 | (   492)
  • from move 112:
Longest game 140 after 1 simulations.
Longest game 146 after 10 simulations.
Longest game 148 after 441 simulations.
Longest game 151 after 2029 simulations.
Longest game 153 after 7167 simulations.
Longest game 157 after 34601 simulations.
Longest game 168 after 41977 simulations.

Results from Sample of 524157 games of Morpion 5T:
Shortest game was 126 moves.
Average game was 136.6568528131838 moves.
Longest game was 168 moves.
Average time/game is 138.334270075569 ms.

Summary of Results every '*' = 5643 games
...
100 | (     0) 
120 | (451482) ********************************************************************************
140 | ( 72673) ************
160 | (     2)
Unfortunately that earlier version of the program was not logging the random number seed used for these games nor was it recording games in a notation that I have a converter for (at this time).

The above were run under "Unicon Version 12.0. July 13, 2011" on Windows 7/x64.