Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
2
Task/Knights-tour/00-META.yaml
Normal file
2
Task/Knights-tour/00-META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
from: http://rosettacode.org/wiki/Knight's_tour
|
||||
20
Task/Knights-tour/00-TASK.txt
Normal file
20
Task/Knights-tour/00-TASK.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
;Task
|
||||
[[wp:Knight%27s_tour|Problem]]: you have a standard 8x8 chessboard, empty but for a single knight on some square. Your task is to emit a series of legal knight moves that result in the knight visiting every square on the chessboard exactly once. Note that it is ''not'' a requirement that the tour be "closed"; that is, the knight need not end within a single move of its start position.
|
||||
|
||||
Input and output may be textual or graphical, according to the conventions of the programming environment. If textual, squares should be indicated in [http://en.wikipedia.org/wiki/Algebraic_chess_notation algebraic notation]. The output should indicate the order in which the knight visits the squares, starting with the initial position. The form of the output may be a diagram of the board with the squares numbered according to visitation sequence, or a textual list of algebraic coordinates in order, or even an actual animation of the knight moving around the chessboard.
|
||||
|
||||
Input: starting square
|
||||
|
||||
Output: move sequence
|
||||
|
||||
|
||||
;Related tasks
|
||||
* [[A* search algorithm]]
|
||||
* [[N-queens problem]]
|
||||
* [[Solve a Hidato puzzle]]
|
||||
* [[Solve a Holy Knight's tour]]
|
||||
* [[Solve a Hopido puzzle]]
|
||||
* [[Solve a Numbrix puzzle]]
|
||||
* [[Solve the no connection puzzle]]
|
||||
<br><br>
|
||||
|
||||
57
Task/Knights-tour/11l/knights-tour.11l
Normal file
57
Task/Knights-tour/11l/knights-tour.11l
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
V _kmoves = [(2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1)]
|
||||
|
||||
F chess2index(=chess, boardsize)
|
||||
‘Convert Algebraic chess notation to internal index format’
|
||||
chess = chess.lowercase()
|
||||
V x = chess[0].code - ‘a’.code
|
||||
V y = boardsize - Int(chess[1..])
|
||||
R (x, y)
|
||||
|
||||
F boardstring(board, boardsize)
|
||||
V r = 0 .< boardsize
|
||||
V lines = ‘’
|
||||
L(y) r
|
||||
lines ‘’= "\n"r.map(x -> (I @board[(x, @y)] {‘#2’.format(@board[(x, @y)])} E ‘ ’)).join(‘,’)
|
||||
R lines
|
||||
|
||||
F knightmoves(board, P, boardsize)
|
||||
V (Px, Py) = P
|
||||
V kmoves = Set(:_kmoves.map((x, y) -> (@Px + x, @Py + y)))
|
||||
kmoves = Set(Array(kmoves).filter((x, y) -> x C 0 .< @boardsize & y C 0 .< @boardsize & !@board[(x, y)]))
|
||||
R kmoves
|
||||
|
||||
F accessibility(board, P, boardsize)
|
||||
[(Int, (Int, Int))] access
|
||||
V brd = copy(board)
|
||||
L(pos) knightmoves(board, P, boardsize' boardsize)
|
||||
brd[pos] = -1
|
||||
access.append((knightmoves(brd, pos, boardsize' boardsize).len, pos))
|
||||
brd[pos] = 0
|
||||
R access
|
||||
|
||||
F knights_tour(start, boardsize, _debug = 0B)
|
||||
[(Int, Int) = Int] board
|
||||
L(x) 0 .< boardsize
|
||||
L(y) 0 .< boardsize
|
||||
board[(x, y)] = 0
|
||||
V move = 1
|
||||
V P = chess2index(start, boardsize)
|
||||
board[P] = move
|
||||
move++
|
||||
I _debug
|
||||
print(boardstring(board, boardsize' boardsize))
|
||||
L move <= board.len
|
||||
P = min(accessibility(board, P, boardsize))[1]
|
||||
board[P] = move
|
||||
move++
|
||||
I _debug
|
||||
print(boardstring(board, boardsize' boardsize))
|
||||
input("\n#2 next: ".format(move))
|
||||
R board
|
||||
|
||||
L(boardsize, start) [(5, ‘c3’), (8, ‘h8’), (10, ‘e6’)]
|
||||
print(‘boardsize: ’boardsize)
|
||||
print(‘Start position: ’start)
|
||||
V board = knights_tour(start, boardsize)
|
||||
print(boardstring(board, boardsize' boardsize))
|
||||
print()
|
||||
255
Task/Knights-tour/360-Assembly/knights-tour.360
Normal file
255
Task/Knights-tour/360-Assembly/knights-tour.360
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
* Knight's tour 20/03/2017
|
||||
KNIGHT CSECT
|
||||
USING KNIGHT,R13 base registers
|
||||
B 72(R15) skip savearea
|
||||
DC 17F'0' savearea
|
||||
STM R14,R12,12(R13) save previous context
|
||||
ST R13,4(R15) link backward
|
||||
ST R15,8(R13) link forward
|
||||
LR R13,R15 set addressability
|
||||
MVC PG(20),=CL20'Knight''s tour ..x..'
|
||||
L R1,NN n
|
||||
XDECO R1,XDEC edit
|
||||
MVC PG+14(2),XDEC+10 n
|
||||
MVC PG+17(2),XDEC+10 n
|
||||
XPRNT PG,L'PG print buffer
|
||||
LA R0,1 1
|
||||
ST R0,X x=1
|
||||
ST R0,Y y=1
|
||||
SR R0,R0 0
|
||||
ST R0,TOTAL total=0
|
||||
LOOP EQU * do loop
|
||||
L R1,X x
|
||||
BCTR R1,0 -1
|
||||
MH R1,NNH *n
|
||||
L R0,Y y
|
||||
BCTR R0,0 -1
|
||||
AR R1,R0 (x-1)*n+y-1
|
||||
SLA R1,1 ((x-1)*n+y-1)*2
|
||||
LA R0,1 1
|
||||
STH R0,BOARD(R1) board(x,y)=1
|
||||
L R2,TOTAL total
|
||||
LA R2,1(R2) total+1
|
||||
STH R2,DISP(R1) disp(x,y)=total+1
|
||||
ST R2,TOTAL total=total+1
|
||||
L R1,X x
|
||||
L R2,Y y
|
||||
BAL R14,CHOOSEMV call choosemv(x,y)
|
||||
C R0,=F'0' until(choosemv(x,y)=0)
|
||||
BNE LOOP loop
|
||||
LA R2,KN*KN n*n
|
||||
IF C,R2,NE,TOTAL THEN if total<>n*n then
|
||||
XPRNT =C'error!!',7 print error
|
||||
ENDIF , endif
|
||||
LA R6,1 i=1
|
||||
DO WHILE=(C,R6,LE,NN) do i=1 to n
|
||||
MVC PG,=CL128' ' init buffer
|
||||
LA R10,PG pgi=0
|
||||
LA R7,1 j=1
|
||||
DO WHILE=(C,R7,LE,NN) do j=1 to n
|
||||
LR R1,R6 i
|
||||
BCTR R1,0 -1
|
||||
MH R1,NNH *n
|
||||
LR R0,R7 j
|
||||
BCTR R0,0 -1
|
||||
AR R1,R0 (i-1)*n+j-1
|
||||
SLA R1,1 ((i-1)*n+j-1)*2
|
||||
LH R2,DISP(R1) disp(i,j)
|
||||
XDECO R2,XDEC edit
|
||||
MVC 0(4,R10),XDEC+8 output
|
||||
LA R10,4(R10) pgi+=4
|
||||
LA R7,1(R7) j++
|
||||
ENDDO , enddo j
|
||||
XPRNT PG,L'PG print buffer
|
||||
LA R6,1(R6) i++
|
||||
ENDDO , enddo i
|
||||
L R13,4(0,R13) restore previous savearea pointer
|
||||
LM R14,R12,12(R13) restore previous context
|
||||
XR R15,R15 return_code=0
|
||||
BR R14 exit
|
||||
*------- ---- ----------------------------------------
|
||||
CHOOSEMV EQU * choosemv(xc,yc)
|
||||
ST R14,SAVEACMV save return point
|
||||
ST R1,XC store xc
|
||||
ST R2,YC store yc
|
||||
MVC MM,=F'9' m=9
|
||||
L R1,XC xc
|
||||
LA R1,1(R1)
|
||||
L R2,YC yc
|
||||
LA R2,2(R2)
|
||||
BAL R14,TRYMV call trymv(xc+1,yc+2)
|
||||
L R1,XC xc
|
||||
LA R1,1(R1)
|
||||
L R2,YC yc
|
||||
SH R2,=H'2'
|
||||
BAL R14,TRYMV call trymv(xc+1,yc-2)
|
||||
L R1,XC xc
|
||||
BCTR R1,0
|
||||
L R2,YC yc
|
||||
LA R2,2(R2)
|
||||
BAL R14,TRYMV call trymv(xc-1,yc+2)
|
||||
L R1,XC xc
|
||||
BCTR R1,0
|
||||
L R2,YC yc
|
||||
SH R2,=H'2'
|
||||
BAL R14,TRYMV call trymv(xc-1,yc-2)
|
||||
L R1,XC xc
|
||||
LA R1,2(R1)
|
||||
L R2,YC yc
|
||||
LA R2,1(R2)
|
||||
BAL R14,TRYMV call trymv(xc+2,yc+1)
|
||||
L R1,XC xc
|
||||
LA R1,2(R1)
|
||||
L R2,YC yc
|
||||
BCTR R2,0
|
||||
BAL R14,TRYMV call trymv(xc+2,yc-1)
|
||||
L R1,XC xc
|
||||
SH R1,=H'2'
|
||||
L R2,YC yc
|
||||
LA R2,1(R2)
|
||||
BAL R14,TRYMV call trymv(xc-2,yc+1)
|
||||
L R1,XC xc
|
||||
SH R1,=H'2'
|
||||
L R2,YC yc
|
||||
BCTR R2,0
|
||||
BAL R14,TRYMV call trymv(xc-2,yc-1)
|
||||
L R4,MM m
|
||||
IF C,R4,EQ,=F'9' THEN if m=9 then
|
||||
LA R0,0 return(0)
|
||||
ELSE , else
|
||||
MVC X,NEWX x=newx
|
||||
MVC Y,NEWY y=newy
|
||||
LA R0,1 return(1)
|
||||
ENDIF , endif
|
||||
L R14,SAVEACMV restore return point
|
||||
BR R14 return
|
||||
SAVEACMV DS A return point
|
||||
*------- ---- ----------------------------------------
|
||||
TRYMV EQU * trymv(xt,yt)
|
||||
ST R14,SAVEATMV save return point
|
||||
ST R1,XT store xt
|
||||
ST R2,YT store yt
|
||||
SR R10,R10 n=0
|
||||
BAL R14,VALIDMV
|
||||
IF LTR,R0,Z,R0 THEN if validmv(xt,yt)=0 then
|
||||
LA R0,0 return(0)
|
||||
B RETURTMV
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
LA R1,1(R1) xt+1
|
||||
L R2,YT
|
||||
LA R2,2(R2) yt+2
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt+1,yt+2)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
LA R1,1(R1) xt+1
|
||||
L R2,YT
|
||||
SH R2,=H'2' yt-2
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt+1,yt-2)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
BCTR R1,0 xt-1
|
||||
L R2,YT
|
||||
LA R2,2(R2) yt+2
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt-1,yt+2)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
BCTR R1,0 xt-1
|
||||
L R2,YT
|
||||
SH R2,=H'2' yt-2
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt-1,yt-2)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
LA R1,2(R1) xt+2
|
||||
L R2,YT
|
||||
LA R2,1(R2) yt+1
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt+2,yt+1)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
LA R1,2(R1) xt+2
|
||||
L R2,YT
|
||||
BCTR R2,0 yt-1
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt+2,yt-1)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
SH R1,=H'2' xt-2
|
||||
L R2,YT
|
||||
LA R2,1(R2) yt+1
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt-2,yt+1)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
L R1,XT
|
||||
SH R1,=H'2' xt-2
|
||||
L R2,YT
|
||||
BCTR R2,0 yt-1
|
||||
BAL R14,VALIDMV
|
||||
IF C,R0,EQ,=F'1' THEN if validmv(xt-2,yt-1)=1 then
|
||||
LA R10,1(R10) n=n+1;
|
||||
ENDIF , endif
|
||||
IF C,R10,LT,MM THEN if n<m then
|
||||
ST R10,MM m=n
|
||||
MVC NEWX,XT newx=xt
|
||||
MVC NEWY,YT newy=yt
|
||||
ENDIF , endif
|
||||
RETURTMV L R14,SAVEATMV restore return point
|
||||
BR R14 return
|
||||
SAVEATMV DS A return point
|
||||
*------- ---- ----------------------------------------
|
||||
VALIDMV EQU * validmv(xv,yv)
|
||||
C R1,=F'1' if xv<1 then
|
||||
BL RET0
|
||||
C R1,NN if xv>nn then
|
||||
BH RET0
|
||||
C R2,=F'1' if yv<1 then
|
||||
BL RET0
|
||||
C R2,NN if yv>nn then
|
||||
BNH OK
|
||||
RET0 SR R0,R0 return(0)
|
||||
B RETURVMV
|
||||
OK LR R3,R1 xv
|
||||
BCTR R3,0
|
||||
MH R3,NNH *n
|
||||
LR R0,R2 yv
|
||||
BCTR R0,0
|
||||
AR R3,R0
|
||||
SLA R3,1
|
||||
LH R4,BOARD(R3) board(xv,yv)
|
||||
IF LTR,R4,Z,R4 THEN if board(xv,yv)=0 then
|
||||
LA R0,1 return(1)
|
||||
ELSE , else
|
||||
SR R0,R0 return(0)
|
||||
ENDIF , endif
|
||||
RETURVMV BR R14 return
|
||||
* ---- ----------------------------------------
|
||||
KN EQU 8 n compile-time
|
||||
NN DC A(KN) n fullword
|
||||
NNH DC AL2(KN) n halfword
|
||||
BOARD DC (KN*KN)H'0' dim board(n,n) init 0
|
||||
DISP DC (KN*KN)H'0' dim disp(n,n) init 0
|
||||
X DS F
|
||||
Y DS F
|
||||
TOTAL DS F
|
||||
XC DS F
|
||||
YC DS F
|
||||
MM DS F
|
||||
NEWX DS F
|
||||
NEWY DS F
|
||||
XT DS F
|
||||
YT DS F
|
||||
XDEC DS CL12
|
||||
PG DC CL128' ' buffer
|
||||
YREGS
|
||||
END KNIGHT
|
||||
288
Task/Knights-tour/ALGOL-68/knights-tour.alg
Normal file
288
Task/Knights-tour/ALGOL-68/knights-tour.alg
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
# Non-recursive Knight's Tour with Warnsdorff's algorithm #
|
||||
# If there are multiple choices, backtrack if the first choice doesn't #
|
||||
# find a solution #
|
||||
|
||||
# the size of the board #
|
||||
INT board size = 8;
|
||||
|
||||
|
||||
# directions for moves #
|
||||
INT nne = 1, nee = 2, see = 3, sse = 4, ssw = 5, sww = 6, nww = 7, nnw = 8;
|
||||
|
||||
INT lowest move = nne;
|
||||
INT highest move = nnw;
|
||||
|
||||
# the vertical position changes of the moves #
|
||||
# nne, nee, see, sse, ssw, sww, nww, nnw #
|
||||
[]INT offset v = ( -2, -1, 1, 2, 2, 1, -1, -2 );
|
||||
# the horizontal position changes of the moves #
|
||||
# nne, nee, see, sse, ssw, sww, nww, nnw #
|
||||
[]INT offset h = ( 1, 2, 2, 1, -1, -2, -2, -1 );
|
||||
|
||||
|
||||
MODE SQUARE = STRUCT( INT move # the number of the move that caused #
|
||||
# the knight to reach this square #
|
||||
, INT direction # the direction of the move that #
|
||||
# brought the knight here - one of #
|
||||
# nne, nee, see, sse, ssw, sww, nww #
|
||||
# or nnw - used for backtracking #
|
||||
# zero for the first move #
|
||||
);
|
||||
|
||||
# the board #
|
||||
[ board size, board size ]SQUARE board;
|
||||
|
||||
# initialises the board so there are no used squares #
|
||||
PROC initialise board = VOID:
|
||||
FOR row FROM 1 LWB board TO 1 UPB board
|
||||
DO
|
||||
FOR col FROM 2 LWB board TO 2 UPB board
|
||||
DO
|
||||
board[ row, col ] := ( 0, 0 )
|
||||
OD
|
||||
OD; # initialise board #
|
||||
|
||||
|
||||
INT iterations := 0;
|
||||
INT backtracks := 0;
|
||||
|
||||
# prints the board #
|
||||
PROC print tour = VOID:
|
||||
BEGIN
|
||||
|
||||
print( ( " a b c d e f g h", newline ) );
|
||||
print( ( " +--------------------------------", newline ) );
|
||||
|
||||
FOR row FROM 1 UPB board BY -1 TO 1 LWB board
|
||||
DO
|
||||
print( ( whole( row, -3 ) ) );
|
||||
print( ( "|" ) );
|
||||
|
||||
FOR col FROM 2 LWB board TO 2 UPB board
|
||||
DO
|
||||
print( ( " " ) );
|
||||
print( ( whole( move OF board[ row, col ], -3 ) ) )
|
||||
OD;
|
||||
print( ( newline ) )
|
||||
OD
|
||||
|
||||
END; # print tour #
|
||||
|
||||
|
||||
# determines whether a move to the specified row and column is possible #
|
||||
PROC can move to = ( INT row, INT col )BOOL:
|
||||
IF row > 1 UPB board
|
||||
OR row < 1 LWB board
|
||||
OR col > 2 UPB board
|
||||
OR col < 2 LWB board
|
||||
THEN
|
||||
# the position is not on the board #
|
||||
FALSE
|
||||
ELSE
|
||||
# the move is legal, check the square is unoccupied #
|
||||
move OF board[ row, col ] = 0
|
||||
FI;
|
||||
|
||||
|
||||
# used to hold counts of the number of moves that could be made in each #
|
||||
# direction from the current square #
|
||||
[ lowest move : highest move ]INT possible move count;
|
||||
|
||||
|
||||
# sets the elements of possible move count to the number of moves that #
|
||||
# could be made in each direction from the specified row and col #
|
||||
PROC count moves in each direction from = ( INT row, INT col )VOID:
|
||||
FOR move direction FROM lowest move TO highest move
|
||||
DO
|
||||
|
||||
INT new row = row + offset v[ move direction ];
|
||||
INT new col = col + offset h[ move direction ];
|
||||
|
||||
IF NOT can move to( new row, new col )
|
||||
THEN
|
||||
# can't move to this square #
|
||||
possible move count[ move direction ] := -1
|
||||
ELSE
|
||||
# a move in this direction is possible #
|
||||
# - count the number of moves that could be made from it #
|
||||
|
||||
possible move count[ move direction ] := 0;
|
||||
|
||||
FOR subsequent move FROM lowest move TO highest move
|
||||
DO
|
||||
IF can move to( new row + offset v[ subsequent move ]
|
||||
, new col + offset h[ subsequent move ]
|
||||
)
|
||||
THEN
|
||||
# have a possible subsequent move #
|
||||
possible move count[ move direction ] +:= 1
|
||||
FI
|
||||
OD
|
||||
FI
|
||||
|
||||
OD;
|
||||
|
||||
|
||||
|
||||
# update the board to the first knight's tour found starting from #
|
||||
# "start row" and "start col". #
|
||||
# return TRUE if one was found, FALSE otherwise #
|
||||
PROC find tour = ( INT start row, INT start col )BOOL:
|
||||
BEGIN
|
||||
|
||||
initialise board;
|
||||
|
||||
BOOL result := TRUE;
|
||||
|
||||
INT move number := 1;
|
||||
INT row := start row;
|
||||
INT col := start col;
|
||||
|
||||
# the tour will be complete when we have made as many moves #
|
||||
# as there squares on the board #
|
||||
INT final move = ( ( ( 1 UPB board ) + 1 ) - 1 LWB board )
|
||||
* ( ( ( 2 UPB board ) + 1 ) - 2 LWB board )
|
||||
;
|
||||
|
||||
# the first move is to place the knight on the starting square #
|
||||
board[ row, col ] := ( move number, lowest move - 1 );
|
||||
# start off with an unknown direction for the best move #
|
||||
INT best direction := lowest move - 1;
|
||||
|
||||
# attempt to find a sequence of moves that will reach each square once #
|
||||
WHILE
|
||||
move number < final move AND result
|
||||
DO
|
||||
|
||||
iterations +:= 1;
|
||||
|
||||
# count the number of moves possible from each possible move #
|
||||
# from this square #
|
||||
count moves in each direction from( row, col );
|
||||
|
||||
# find the direction with the lowest number of subsequent moves #
|
||||
|
||||
IF best direction < lowest move
|
||||
THEN
|
||||
# must find the best direction to move in #
|
||||
|
||||
INT lowest move count := highest move + 1;
|
||||
|
||||
FOR move direction FROM lowest move TO highest move
|
||||
DO
|
||||
IF possible move count[ move direction ] >= 0
|
||||
AND possible move count[ move direction ] < lowest move count
|
||||
THEN
|
||||
# have a move with fewer possible subsequent moves #
|
||||
best direction := move direction;
|
||||
lowest move count := possible move count[ move direction ]
|
||||
FI
|
||||
OD
|
||||
|
||||
ELSE
|
||||
# following a backtrack - find an alternative with the same #
|
||||
# lowest number of possible moves - if there are any #
|
||||
# if there aren't, we will backtrack again #
|
||||
|
||||
INT lowest move count := possible move count[ best direction ];
|
||||
|
||||
WHILE
|
||||
best direction +:= 1;
|
||||
IF best direction > highest move
|
||||
THEN
|
||||
# no more possible moves with the lowest number of #
|
||||
# subsequent moves #
|
||||
FALSE
|
||||
ELSE
|
||||
# keep looking if the number of moves from this square #
|
||||
# isn't the lowest #
|
||||
possible move count[ best direction ] /= lowest move count
|
||||
FI
|
||||
DO
|
||||
SKIP
|
||||
OD
|
||||
|
||||
FI;
|
||||
|
||||
IF best direction <= highest move
|
||||
AND best direction >= lowest move
|
||||
THEN
|
||||
# we found a best possible move #
|
||||
|
||||
INT new row = row + offset v[ best direction ];
|
||||
INT new col = col + offset h[ best direction ];
|
||||
|
||||
row := new row;
|
||||
col := new col;
|
||||
move number +:= 1;
|
||||
board[ row, col ] := ( move number, best direction );
|
||||
|
||||
best direction := lowest move - 1
|
||||
|
||||
ELSE
|
||||
# no more moves from this position - backtrack #
|
||||
|
||||
IF move number = 1
|
||||
THEN
|
||||
# at the starting position - no solution #
|
||||
result := FALSE
|
||||
|
||||
ELSE
|
||||
# not at the starting position - undo the latest move #
|
||||
|
||||
backtracks +:= 1;
|
||||
|
||||
move number -:= 1;
|
||||
|
||||
INT curr row := row;
|
||||
INT curr col := col;
|
||||
|
||||
best direction := direction OF board[ curr row, curr col ];
|
||||
|
||||
row -:= offset v[ best direction ];
|
||||
col -:= offset h[ best direction ];
|
||||
|
||||
# reset the square we just backtracked from #
|
||||
board[ curr row, curr col ] := ( 0, 0 )
|
||||
|
||||
FI
|
||||
|
||||
FI
|
||||
|
||||
OD;
|
||||
|
||||
result
|
||||
END; # find tour #
|
||||
|
||||
|
||||
main:(
|
||||
|
||||
# get the starting position #
|
||||
|
||||
CHAR row;
|
||||
CHAR col;
|
||||
|
||||
WHILE
|
||||
print( ( "Enter starting row(1-8) and col(a-h): " ) );
|
||||
read ( ( row, col, newline ) );
|
||||
row < "1" OR row > "8" OR col < "a" OR col > "h"
|
||||
DO
|
||||
SKIP
|
||||
OD;
|
||||
|
||||
# calculate the tour from that position, if possible #
|
||||
|
||||
IF find tour( ABS row - ABS "0", ( ABS col - ABS "a" ) + 1 )
|
||||
THEN
|
||||
# found a solution #
|
||||
print tour
|
||||
ELSE
|
||||
# couldn't find a solution #
|
||||
print( ( "Solution not found - iterations: ", iterations
|
||||
, ", backtracks: ", backtracks
|
||||
, newline
|
||||
)
|
||||
)
|
||||
FI
|
||||
|
||||
)
|
||||
75
Task/Knights-tour/ANSI-BASIC/knights-tour.basic
Normal file
75
Task/Knights-tour/ANSI-BASIC/knights-tour.basic
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
100 DECLARE EXTERNAL FUNCTION choosemove
|
||||
110 !
|
||||
120 RANDOMIZE
|
||||
130 PUBLIC NUMERIC X, Y, TRUE, FALSE
|
||||
140 LET TRUE = -1
|
||||
150 LET FALSE = 0
|
||||
160 !
|
||||
170 SET WINDOW 1,512,1,512
|
||||
180 SET AREA COLOR "black"
|
||||
190 FOR x=0 TO 512-128 STEP 128
|
||||
200 FOR y=0 TO 512-128 STEP 128
|
||||
210 PLOT AREA:x+64,y;x+128,y;x+128,y+64;x+64,y+64
|
||||
220 PLOT AREA:x,y+64;x+64,y+64;x+64,y+128;x,y+128
|
||||
230 NEXT y
|
||||
240 NEXT x
|
||||
250 !
|
||||
260 SET LINE COLOR "red"
|
||||
270 SET LINE WIDTH 6
|
||||
280 !
|
||||
290 PUBLIC NUMERIC Board(0 TO 7,0 TO 7)
|
||||
300 LET X = 0
|
||||
310 LET Y = 0
|
||||
320 LET Total = 0
|
||||
330 DO
|
||||
340 LET Board(X,Y) = TRUE
|
||||
350 PLOT LINES: X*64+32,Y*64+32;
|
||||
360 LET Total = Total + 1
|
||||
370 LOOP UNTIL choosemove(X, Y) = FALSE
|
||||
380 IF Total <> 64 THEN STOP
|
||||
390 END
|
||||
400 !
|
||||
410 EXTERNAL FUNCTION choosemove(X1, Y1)
|
||||
420 DECLARE EXTERNAL SUB trymove
|
||||
430 LET M = 9
|
||||
440 CALL trymove(X1+1, Y1+2, M, newx, newy)
|
||||
450 CALL trymove(X1+1, Y1-2, M, newx, newy)
|
||||
460 CALL trymove(X1-1, Y1+2, M, newx, newy)
|
||||
470 CALL trymove(X1-1, Y1-2, M, newx, newy)
|
||||
480 CALL trymove(X1+2, Y1+1, M, newx, newy)
|
||||
490 CALL trymove(X1+2, Y1-1, M, newx, newy)
|
||||
500 CALL trymove(X1-2, Y1+1, M, newx, newy)
|
||||
510 CALL trymove(X1-2, Y1-1, M, newx, newy)
|
||||
520 IF M=9 THEN
|
||||
530 LET choosemove = FALSE
|
||||
540 EXIT FUNCTION
|
||||
550 END IF
|
||||
560 LET X = newx
|
||||
570 LET Y = newy
|
||||
580 LET choosemove = TRUE
|
||||
590 END FUNCTION
|
||||
600 !
|
||||
610 EXTERNAL SUB trymove(X, Y, M, newx, newy)
|
||||
620 !
|
||||
630 DECLARE EXTERNAL FUNCTION validmove
|
||||
640 IF validmove(X,Y) = 0 THEN EXIT SUB
|
||||
650 IF validmove(X+1,Y+2) <> 0 THEN LET N = N + 1
|
||||
660 IF validmove(X+1,Y-2) <> 0 THEN LET N = N + 1
|
||||
670 IF validmove(X-1,Y+2) <> 0 THEN LET N = N + 1
|
||||
680 IF validmove(X-1,Y-2) <> 0 THEN LET N = N + 1
|
||||
690 IF validmove(X+2,Y+1) <> 0 THEN LET N = N + 1
|
||||
700 IF validmove(X+2,Y-1) <> 0 THEN LET N = N + 1
|
||||
710 IF validmove(X-2,Y+1) <> 0 THEN LET N = N + 1
|
||||
720 IF validmove(X-2,Y-1) <> 0 THEN LET N = N + 1
|
||||
730 IF N>M THEN EXIT SUB
|
||||
740 IF N=M AND RND<.5 THEN EXIT SUB
|
||||
750 LET M = N
|
||||
760 LET newx = X
|
||||
770 LET newy = Y
|
||||
780 END SUB
|
||||
790 !
|
||||
800 EXTERNAL FUNCTION validmove(X,Y)
|
||||
810 LET validmove = FALSE
|
||||
820 IF X<0 OR X>7 OR Y<0 OR Y>7 THEN EXIT FUNCTION
|
||||
830 IF Board(X,Y)=FALSE THEN LET validmove = TRUE
|
||||
840 END FUNCTION
|
||||
727
Task/Knights-tour/ATS/knights-tour.ats
Normal file
727
Task/Knights-tour/ATS/knights-tour.ats
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
(*
|
||||
Find Knight’s Tours.
|
||||
|
||||
Using Warnsdorff’s heuristic, find multiple solutions.
|
||||
Optionally accept only closed tours.
|
||||
|
||||
Compile with:
|
||||
patscc -O3 -DATS_MEMALLOC_GCBDW -o knights_tour knights_tour.dats -lgc
|
||||
|
||||
Usage: ./knights_tour [START_POSITION [MAX_TOURS [closed]]]
|
||||
Examples:
|
||||
./knights_tour (prints one tour starting from a1)
|
||||
./knights_tour c5
|
||||
./knights_tour c5 2000
|
||||
./knights_tour c5 2000 closed
|
||||
*)
|
||||
|
||||
#define ATS_DYNLOADFLAG 0 (* No initialization is needed. *)
|
||||
|
||||
#include "share/atspre_define.hats"
|
||||
#include "share/atspre_staload.hats"
|
||||
|
||||
#define EMPTY_SQUARE ~1
|
||||
macdef nil_move = @(~1, ~1)
|
||||
|
||||
fn
|
||||
int_right_justified
|
||||
{i : int}
|
||||
{n : int | 0 <= n; n < 100}
|
||||
(i : int i,
|
||||
n : int n) :
|
||||
string =
|
||||
let
|
||||
var buffer : @[char][100] = @[char][100] ('\0')
|
||||
val _ = $extfcall (int, "snprintf", buffer, 100, "%*i", n, i)
|
||||
in
|
||||
strnptr2string (string1_copy ($UNSAFE.cast{string n} buffer))
|
||||
end
|
||||
|
||||
typedef move_t (i : int,
|
||||
j : int) =
|
||||
@(int i, int j)
|
||||
typedef move_t =
|
||||
[i, j : int]
|
||||
move_t (i, j)
|
||||
|
||||
fn
|
||||
move_t_is_nil (move : move_t) :<>
|
||||
bool =
|
||||
let
|
||||
val @(i, j) = move
|
||||
val @(i_nil, j_nil) = nil_move
|
||||
in
|
||||
(i = i_nil && j = j_nil)
|
||||
end
|
||||
|
||||
fn
|
||||
move_t_fprint (f : FILEref,
|
||||
move : move_t) :
|
||||
void =
|
||||
let
|
||||
val @(i, j) = move
|
||||
val letter = char2i 'a' + j - 1
|
||||
val digit = char2i '0' + i
|
||||
in
|
||||
fileref_putc (f, letter);
|
||||
fileref_putc (f, digit);
|
||||
end
|
||||
|
||||
vtypedef chessboard_vt (t : t@ype,
|
||||
n_ranks : int,
|
||||
n_files : int,
|
||||
p : addr) =
|
||||
@{
|
||||
pf_board = @[t][n_ranks * n_files] @ p |
|
||||
n_ranks = uint n_ranks,
|
||||
n_files = uint n_files,
|
||||
n_squares = uint (n_ranks * n_files),
|
||||
p_board = ptr p
|
||||
}
|
||||
vtypedef chessboard_vt (t : t@ype,
|
||||
n_ranks : int,
|
||||
n_files : int) =
|
||||
[p : addr]
|
||||
chessboard_vt (t, n_ranks, n_files, p)
|
||||
vtypedef chessboard_vt (t : t@ype) =
|
||||
[n_ranks, n_files : int]
|
||||
chessboard_vt (t, n_ranks, n_files)
|
||||
|
||||
fn {t : t@ype}
|
||||
chessboard_vt_make
|
||||
{n_ranks, n_files : pos}
|
||||
(n_ranks : uint n_ranks,
|
||||
n_files : uint n_files,
|
||||
fill : t) :
|
||||
chessboard_vt (t, n_ranks, n_files) =
|
||||
let
|
||||
val size = u2sz (n_ranks * n_files)
|
||||
val @(pf, pfgc | p) = array_ptr_alloc<t> (size)
|
||||
val _ = array_initize_elt<t> (!p, size, fill)
|
||||
prval _ = mfree_gc_v_elim pfgc (* Let the memory leak. *)
|
||||
in
|
||||
@{
|
||||
pf_board = pf |
|
||||
n_ranks = n_ranks,
|
||||
n_files = n_files,
|
||||
n_squares = n_ranks * n_files,
|
||||
p_board = p
|
||||
}
|
||||
end
|
||||
|
||||
fn {t : t@ype}
|
||||
chessboard_vt_get
|
||||
{n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
(chessboard : !chessboard_vt (t, n_ranks, n_files),
|
||||
i : int i,
|
||||
j : int j) :
|
||||
t =
|
||||
let
|
||||
val index = (i - 1) + (u2i (chessboard.n_ranks) * (j - 1))
|
||||
val _ = assertloc (0 <= index)
|
||||
val _ = assertloc (index < u2i (chessboard.n_squares))
|
||||
in
|
||||
array_get_at (!(chessboard.p_board), index)
|
||||
end
|
||||
|
||||
fn {t : t@ype}
|
||||
chessboard_vt_set
|
||||
{n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
(chessboard : !chessboard_vt (t, n_ranks, n_files),
|
||||
i : int i,
|
||||
j : int j,
|
||||
value : t) :
|
||||
void =
|
||||
let
|
||||
val index = (i - 1) + (u2i (chessboard.n_ranks) * (j - 1))
|
||||
val _ = assertloc (0 <= index)
|
||||
val _ = assertloc (index < u2i (chessboard.n_squares))
|
||||
in
|
||||
array_set_at (!(chessboard.p_board), index, value)
|
||||
end
|
||||
|
||||
extern fn {t : t@ype}
|
||||
find_nth_position$equal (x : t,
|
||||
y : t) :
|
||||
bool
|
||||
|
||||
fn {t : t@ype}
|
||||
find_nth_position
|
||||
{n_ranks, n_files : pos}
|
||||
(chessboard : !chessboard_vt (t, n_ranks, n_files),
|
||||
n : t) :
|
||||
[i, j : int]
|
||||
move_t (i, j) =
|
||||
let
|
||||
val n_ranks = chessboard.n_ranks
|
||||
val n_files = chessboard.n_files
|
||||
|
||||
fun
|
||||
outer_loop {i : pos | i <= n_ranks + 1} .<n_ranks + 1 - i>.
|
||||
(chessboard : !chessboard_vt (t, n_ranks, n_files),
|
||||
i : int i) :
|
||||
[i, j : int]
|
||||
move_t (i, j) =
|
||||
let
|
||||
fun
|
||||
inner_loop {j : pos | j <= n_files + 1} .<n_files + 1 - j>.
|
||||
(chessboard : !chessboard_vt (t, n_ranks, n_files),
|
||||
j : int j) :
|
||||
[j : int]
|
||||
int j =
|
||||
if u2i n_files < j then
|
||||
j
|
||||
else
|
||||
let
|
||||
val v = chessboard_vt_get<t> (chessboard, i, j)
|
||||
in
|
||||
if find_nth_position$equal<t> (n, v) then
|
||||
j
|
||||
else
|
||||
inner_loop (chessboard, succ j)
|
||||
end
|
||||
in
|
||||
if u2i n_ranks < i then
|
||||
nil_move
|
||||
else
|
||||
let
|
||||
val j = inner_loop (chessboard, 1)
|
||||
in
|
||||
if j <= u2i n_files then
|
||||
@(i, j)
|
||||
else
|
||||
outer_loop (chessboard, succ i)
|
||||
end
|
||||
end
|
||||
in
|
||||
outer_loop (chessboard, 1)
|
||||
end
|
||||
|
||||
implement
|
||||
find_nth_position$equal<int> (x, y) =
|
||||
x = y
|
||||
|
||||
fn
|
||||
knights_tour_is_closed
|
||||
{n_ranks, n_files : pos}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files)) :
|
||||
bool =
|
||||
let
|
||||
val n_squares = chessboard.n_squares
|
||||
val @(i1, j1) = find_nth_position<int> (chessboard, 1)
|
||||
val @(i2, j2) = find_nth_position<int> (chessboard, u2i n_squares)
|
||||
val i_diff = abs (i1 - i2)
|
||||
val j_diff = abs (j1 - j2)
|
||||
in
|
||||
(i_diff = 1 && j_diff = 2) || (i_diff = 2 && j_diff = 1)
|
||||
end
|
||||
|
||||
fn
|
||||
knights_tour_board_fprint
|
||||
{n_ranks, n_files : pos}
|
||||
(f : FILEref,
|
||||
chessboard : !chessboard_vt (int, n_ranks, n_files)) :
|
||||
void =
|
||||
{
|
||||
val n_ranks = chessboard.n_ranks
|
||||
val n_files = chessboard.n_files
|
||||
|
||||
fun
|
||||
outer_loop {i : int | 0 <= i; i <= n_ranks} .<i>.
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
i : int i) :
|
||||
void =
|
||||
if 0 < i then
|
||||
{
|
||||
val _ = fileref_puts (f, " ")
|
||||
val _ =
|
||||
let
|
||||
var j : [j : int] int j
|
||||
in
|
||||
for (j := 1; j <= u2i n_files; j := succ j)
|
||||
fileref_puts (f, "+----")
|
||||
end
|
||||
val _ = fileref_puts (f, "+\n")
|
||||
val _ = fileref_puts (f, int_right_justified (i, 2))
|
||||
val _ = fileref_puts (f, " ")
|
||||
|
||||
fun
|
||||
inner_loop {j : int | 1 <= j; j <= n_files + 1}
|
||||
(chessboard : !chessboard_vt (int, n_ranks,
|
||||
n_files),
|
||||
j : int j) :
|
||||
void =
|
||||
if j <= u2i n_files then
|
||||
{
|
||||
val v = chessboard_vt_get<int> (chessboard, i, j)
|
||||
val v = g1ofg0 v
|
||||
val _ = fileref_puts (f, " | ")
|
||||
val _ =
|
||||
if v = EMPTY_SQUARE then
|
||||
fileref_puts (f, " ")
|
||||
else
|
||||
fileref_puts (f, int_right_justified (g1ofg0 v, 2))
|
||||
val _ = inner_loop (chessboard, succ j)
|
||||
}
|
||||
|
||||
val _ = inner_loop (chessboard, 1)
|
||||
val _ = fileref_puts (f, " |\n")
|
||||
|
||||
val _ = outer_loop (chessboard, pred i)
|
||||
}
|
||||
|
||||
val _ = outer_loop (chessboard, u2i n_ranks)
|
||||
val _ = fileref_puts (f, " ")
|
||||
val _ =
|
||||
let
|
||||
var j : [j : int] int j
|
||||
in
|
||||
for (j := 1; j <= u2i n_files; j := succ j)
|
||||
fileref_puts (f, "+----")
|
||||
end
|
||||
val _ = fileref_puts (f, "+\n")
|
||||
val _ = fileref_puts (f, " ")
|
||||
val _ =
|
||||
let
|
||||
var j : [j : int] int j
|
||||
in
|
||||
for (j := 1; j <= u2i n_files; j := succ j)
|
||||
let
|
||||
val letter = char2i 'a' + j - 1
|
||||
in
|
||||
fileref_puts (f, " ");
|
||||
fileref_putc (f, letter)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
fn
|
||||
knights_tour_moves_fprint
|
||||
{n_ranks, n_files : pos}
|
||||
(f : FILEref,
|
||||
chessboard : !chessboard_vt (int, n_ranks, n_files)) :
|
||||
void =
|
||||
{
|
||||
prval _ = mul_pos_pos_pos (mul_make {n_ranks, n_files} ())
|
||||
|
||||
val n_ranks = chessboard.n_ranks
|
||||
val n_files = chessboard.n_files
|
||||
val n_squares = chessboard.n_squares
|
||||
|
||||
val @(pf, pfgc | p_positions) =
|
||||
array_ptr_alloc<move_t> (u2sz n_squares)
|
||||
val _ = array_initize_elt<move_t> (!p_positions, u2sz n_squares,
|
||||
nil_move)
|
||||
|
||||
macdef positions = !p_positions
|
||||
|
||||
fun
|
||||
loop {k : int | 0 <= k; k <= n_ranks * n_files}
|
||||
.<n_ranks * n_files - k>.
|
||||
(positions : &(@[move_t][n_ranks * n_files]),
|
||||
chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
k : int k) :
|
||||
void =
|
||||
if k < u2i n_squares then
|
||||
{
|
||||
val i = u2i ((i2u k) mod n_ranks) + 1
|
||||
val j = u2i ((i2u k) / n_ranks) + 1
|
||||
val v = chessboard_vt_get<int> (chessboard, i, j)
|
||||
val v = g1ofg0 v
|
||||
val _ = assertloc (1 <= v)
|
||||
val _ = assertloc (v <= u2i n_squares)
|
||||
val _ = positions[v - 1] := @(i, j)
|
||||
val _ = loop (positions, chessboard, succ k)
|
||||
}
|
||||
val _ = loop (positions, chessboard, 0)
|
||||
|
||||
fun
|
||||
loop {k : int | 0 <= k; k < n_ranks * n_files}
|
||||
.<n_ranks * n_files - k>.
|
||||
(positions : &(@[move_t][n_ranks * n_files]),
|
||||
k : int k) :
|
||||
void =
|
||||
if k < u2i (pred n_squares) then
|
||||
{
|
||||
val _ = move_t_fprint (f, positions[k])
|
||||
val line_end = (((i2u (k + 1)) mod n_files) = 0U)
|
||||
val _ =
|
||||
fileref_puts (f, (if line_end then " ->\n" else " -> "))
|
||||
val _ = loop (positions, succ k)
|
||||
}
|
||||
val _ = loop (positions, 0)
|
||||
val _ = move_t_fprint (f, positions[pred n_squares])
|
||||
val _ =
|
||||
if knights_tour_is_closed (chessboard) then
|
||||
fileref_puts (f, " -> cycle")
|
||||
|
||||
val _ = array_ptr_free (pf, pfgc | p_positions)
|
||||
}
|
||||
|
||||
typedef knights_moves_t =
|
||||
@(move_t, move_t, move_t, move_t,
|
||||
move_t, move_t, move_t, move_t)
|
||||
|
||||
fn
|
||||
possible_moves {n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
i : int i,
|
||||
j : int j) :
|
||||
knights_moves_t =
|
||||
let
|
||||
fn
|
||||
try_move {istride, jstride : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
istride : int istride,
|
||||
jstride : int jstride) :
|
||||
move_t =
|
||||
let
|
||||
val i1 = i + istride
|
||||
val j1 = j + jstride
|
||||
in
|
||||
if i1 < 1 then
|
||||
nil_move
|
||||
else if u2i (chessboard.n_ranks) < i1 then
|
||||
nil_move
|
||||
else if j1 < 1 then
|
||||
nil_move
|
||||
else if u2i (chessboard.n_files) < j1 then
|
||||
nil_move
|
||||
else
|
||||
let
|
||||
val v = chessboard_vt_get (chessboard, i1, j1) : int
|
||||
in
|
||||
if v <> EMPTY_SQUARE then
|
||||
nil_move
|
||||
else
|
||||
@(i1, j1)
|
||||
end
|
||||
end
|
||||
|
||||
val move0 = try_move (chessboard, 1, 2)
|
||||
val move1 = try_move (chessboard, 2, 1)
|
||||
val move2 = try_move (chessboard, 1, ~2)
|
||||
val move3 = try_move (chessboard, 2, ~1)
|
||||
val move4 = try_move (chessboard, ~1, 2)
|
||||
val move5 = try_move (chessboard, ~2, 1)
|
||||
val move6 = try_move (chessboard, ~1, ~2)
|
||||
val move7 = try_move (chessboard, ~2, ~1)
|
||||
in
|
||||
@(move0, move1, move2, move3, move4, move5, move6, move7)
|
||||
end
|
||||
|
||||
fn
|
||||
count_following_moves
|
||||
{n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
{n_position : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
move : move_t (i, j),
|
||||
n_position : int n_position) :
|
||||
uint =
|
||||
if move_t_is_nil move then
|
||||
0U
|
||||
else
|
||||
let
|
||||
fn
|
||||
succ_if_move_is_not_nil
|
||||
{i, j : int}
|
||||
(w : uint,
|
||||
move : move_t (i, j)) :<>
|
||||
uint =
|
||||
if move_t_is_nil move then
|
||||
w
|
||||
else
|
||||
succ w
|
||||
|
||||
val @(i, j) = move
|
||||
val _ = chessboard_vt_set<int> (chessboard, i, j,
|
||||
succ n_position)
|
||||
val following_moves = possible_moves (chessboard, i, j)
|
||||
|
||||
val w = 0U
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.0)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.1)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.2)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.3)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.4)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.5)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.6)
|
||||
val w = succ_if_move_is_not_nil (w, following_moves.7)
|
||||
|
||||
val _ = chessboard_vt_set<int> (chessboard, i, j, EMPTY_SQUARE)
|
||||
in
|
||||
w
|
||||
end
|
||||
|
||||
fn
|
||||
pick_w (w0 : uint,
|
||||
w1 : uint,
|
||||
w2 : uint,
|
||||
w3 : uint,
|
||||
w4 : uint,
|
||||
w5 : uint,
|
||||
w6 : uint,
|
||||
w7 : uint) :<>
|
||||
uint =
|
||||
let
|
||||
fn
|
||||
next_pick (u : uint,
|
||||
v : uint) :<>
|
||||
uint =
|
||||
if v = 0U then
|
||||
u
|
||||
else if u = 0U then
|
||||
v
|
||||
else
|
||||
min (u, v)
|
||||
|
||||
val w = 0U
|
||||
val w = next_pick (w, w0)
|
||||
val w = next_pick (w, w1)
|
||||
val w = next_pick (w, w2)
|
||||
val w = next_pick (w, w3)
|
||||
val w = next_pick (w, w4)
|
||||
val w = next_pick (w, w5)
|
||||
val w = next_pick (w, w6)
|
||||
val w = next_pick (w, w7)
|
||||
in
|
||||
w
|
||||
end
|
||||
|
||||
fn
|
||||
next_moves {n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
{n_position : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
i : int i,
|
||||
j : int j,
|
||||
n_position : int n_position) :
|
||||
knights_moves_t =
|
||||
(* Prune and sort the moves according to Warnsdorff’s heuristic,
|
||||
keeping only moves that have the minimum number of legal
|
||||
following moves. *)
|
||||
let
|
||||
val moves = possible_moves (chessboard, i, j)
|
||||
val w0 = count_following_moves (chessboard, moves.0, n_position)
|
||||
val w1 = count_following_moves (chessboard, moves.1, n_position)
|
||||
val w2 = count_following_moves (chessboard, moves.2, n_position)
|
||||
val w3 = count_following_moves (chessboard, moves.3, n_position)
|
||||
val w4 = count_following_moves (chessboard, moves.4, n_position)
|
||||
val w5 = count_following_moves (chessboard, moves.5, n_position)
|
||||
val w6 = count_following_moves (chessboard, moves.6, n_position)
|
||||
val w7 = count_following_moves (chessboard, moves.7, n_position)
|
||||
val w = pick_w (w0, w1, w2, w3, w4, w5, w6, w7)
|
||||
in
|
||||
if w = 0U then
|
||||
@(nil_move, nil_move, nil_move, nil_move,
|
||||
nil_move, nil_move, nil_move, nil_move)
|
||||
else
|
||||
@(if w0 = w then moves.0 else nil_move,
|
||||
if w1 = w then moves.1 else nil_move,
|
||||
if w2 = w then moves.2 else nil_move,
|
||||
if w3 = w then moves.3 else nil_move,
|
||||
if w4 = w then moves.4 else nil_move,
|
||||
if w5 = w then moves.5 else nil_move,
|
||||
if w6 = w then moves.6 else nil_move,
|
||||
if w7 = w then moves.7 else nil_move)
|
||||
end
|
||||
|
||||
fn
|
||||
make_and_fprint_tours
|
||||
{n_ranks, n_files : int}
|
||||
{i, j : int}
|
||||
{max_tours : int}
|
||||
(f : FILEref,
|
||||
n_ranks : int n_ranks,
|
||||
n_files : int n_files,
|
||||
i : int i,
|
||||
j : int j,
|
||||
max_tours : int max_tours,
|
||||
closed_only : bool) :
|
||||
void =
|
||||
{
|
||||
val n_ranks = max (1, n_ranks)
|
||||
val n_files = max (1, n_files)
|
||||
val i = max (1, min (n_ranks, i))
|
||||
val j = max (1, min (n_files, j))
|
||||
val max_tours = max (1, max_tours)
|
||||
|
||||
val n_ranks = i2u n_ranks
|
||||
val n_files = i2u n_files
|
||||
|
||||
val i_start = i
|
||||
val j_start = j
|
||||
|
||||
var tours_printed : int = 0
|
||||
|
||||
val chessboard =
|
||||
chessboard_vt_make<int> (n_ranks, n_files, g1ofg0 EMPTY_SQUARE)
|
||||
|
||||
fun
|
||||
explore {n_ranks, n_files : pos}
|
||||
{i, j : int}
|
||||
{n_position : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks, n_files),
|
||||
i : int i,
|
||||
j : int j,
|
||||
n_position : int n_position,
|
||||
tours_printed : &int) :
|
||||
void =
|
||||
if tours_printed < max_tours then
|
||||
let
|
||||
fn
|
||||
print_board {i1, j1 : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks,
|
||||
n_files),
|
||||
tours_printed : &int) :
|
||||
void =
|
||||
begin
|
||||
tours_printed := succ tours_printed;
|
||||
fprintln! (f, "Tour number ", tours_printed);
|
||||
knights_tour_moves_fprint (f, chessboard);
|
||||
fprintln! (f);
|
||||
knights_tour_board_fprint (f, chessboard);
|
||||
fprintln! (f);
|
||||
fprintln! (f)
|
||||
end
|
||||
|
||||
fn
|
||||
satisfies_closedness
|
||||
{i1, j1 : int}
|
||||
(move : move_t (i1, j1)) :
|
||||
bool =
|
||||
if closed_only then
|
||||
let
|
||||
val @(i1, j1) = move
|
||||
val i_diff = abs (i1 - i_start)
|
||||
val j_diff = abs (j1 - j_start)
|
||||
in
|
||||
(i_diff = 1 && j_diff = 2)
|
||||
|| (i_diff = 2 && j_diff = 1)
|
||||
end
|
||||
else
|
||||
true
|
||||
|
||||
fn
|
||||
try_last_move
|
||||
{i1, j1 : int}
|
||||
(chessboard : !chessboard_vt (int, n_ranks,
|
||||
n_files),
|
||||
move : move_t (i1, j1),
|
||||
tours_printed : &int) :
|
||||
void =
|
||||
if ~move_t_is_nil move && satisfies_closedness move then
|
||||
let
|
||||
val @(i1, j1) = move
|
||||
in
|
||||
chessboard_vt_set<int> (chessboard, i1, j1,
|
||||
n_position + 1);
|
||||
print_board (chessboard, tours_printed);
|
||||
chessboard_vt_set<int> (chessboard, i1, j1,
|
||||
EMPTY_SQUARE)
|
||||
end
|
||||
|
||||
fun
|
||||
explore_inner (chessboard : !chessboard_vt (int, n_ranks,
|
||||
n_files),
|
||||
tours_printed : &int) :
|
||||
void =
|
||||
if u2i (chessboard.n_squares) - n_position = 1 then
|
||||
(* Is the last move possible? If so, make it and print
|
||||
the board. (Only zero or one of the moves can be
|
||||
non-nil.) *)
|
||||
let
|
||||
val moves = possible_moves (chessboard, i, j)
|
||||
in
|
||||
try_last_move (chessboard, moves.0, tours_printed);
|
||||
try_last_move (chessboard, moves.1, tours_printed);
|
||||
try_last_move (chessboard, moves.2, tours_printed);
|
||||
try_last_move (chessboard, moves.3, tours_printed);
|
||||
try_last_move (chessboard, moves.4, tours_printed);
|
||||
try_last_move (chessboard, moves.5, tours_printed);
|
||||
try_last_move (chessboard, moves.6, tours_printed);
|
||||
try_last_move (chessboard, moves.7, tours_printed)
|
||||
end
|
||||
else
|
||||
let
|
||||
val moves = next_moves (chessboard, i, j, n_position)
|
||||
macdef explore_move (move) =
|
||||
begin
|
||||
if ~move_t_is_nil ,(move) then
|
||||
explore (chessboard, (,(move)).0, (,(move)).1,
|
||||
succ n_position, tours_printed)
|
||||
end
|
||||
in
|
||||
explore_move (moves.0);
|
||||
explore_move (moves.1);
|
||||
explore_move (moves.2);
|
||||
explore_move (moves.3);
|
||||
explore_move (moves.4);
|
||||
explore_move (moves.5);
|
||||
explore_move (moves.6);
|
||||
explore_move (moves.7)
|
||||
end
|
||||
in
|
||||
chessboard_vt_set<int> (chessboard, i, j, n_position);
|
||||
explore_inner (chessboard, tours_printed);
|
||||
chessboard_vt_set<int> (chessboard, i, j, EMPTY_SQUARE)
|
||||
end
|
||||
|
||||
val _ = explore (chessboard, i, j, 1, tours_printed)
|
||||
|
||||
val _ = $UNSAFE.castvwtp0{void} chessboard
|
||||
}
|
||||
|
||||
fn
|
||||
algebraic_notation_to_move (s : string) :
|
||||
move_t =
|
||||
let
|
||||
val s = g1ofg0 s
|
||||
val n = string_length s
|
||||
in
|
||||
if n = 2 then
|
||||
let
|
||||
val i = g1ofg0 (char2i (s[1]) - char2i ('0'))
|
||||
val j = g1ofg0 (char2i (s[0]) - char2i ('a') + 1)
|
||||
in
|
||||
@(i, j)
|
||||
end
|
||||
else
|
||||
@(1, 1)
|
||||
end
|
||||
|
||||
implement
|
||||
main0 (argc, argv) =
|
||||
{
|
||||
val @(i, j) =
|
||||
begin
|
||||
if 2 <= argc then
|
||||
algebraic_notation_to_move (argv[1])
|
||||
else
|
||||
@(1, 1)
|
||||
end : move_t
|
||||
|
||||
val max_tours =
|
||||
begin
|
||||
if 3 <= argc then
|
||||
$extfcall (int, "atoi", argv[2])
|
||||
else
|
||||
1
|
||||
end : int
|
||||
val max_tours = g1ofg0 max_tours
|
||||
|
||||
val closed_only =
|
||||
begin
|
||||
if 4 <= argc then
|
||||
argv[3] = "closed"
|
||||
else
|
||||
false
|
||||
end : bool
|
||||
|
||||
val _ = make_and_fprint_tours (stdout_ref, 8, 8, i, j, max_tours,
|
||||
closed_only)
|
||||
}
|
||||
68
Task/Knights-tour/AWK/knights-tour.awk
Normal file
68
Task/Knights-tour/AWK/knights-tour.awk
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# syntax: GAWK -f KNIGHTS_TOUR.AWK [-v sr=x] [-v sc=x]
|
||||
#
|
||||
# examples:
|
||||
# GAWK -f KNIGHTS_TOUR.AWK (default)
|
||||
# GAWK -f KNIGHTS_TOUR.AWK -v sr=1 -v sc=1 start at top left (default)
|
||||
# GAWK -f KNIGHTS_TOUR.AWK -v sr=1 -v sc=8 start at top right
|
||||
# GAWK -f KNIGHTS_TOUR.AWK -v sr=8 -v sc=8 start at bottom right
|
||||
# GAWK -f KNIGHTS_TOUR.AWK -v sr=8 -v sc=1 start at bottom left
|
||||
#
|
||||
BEGIN {
|
||||
N = 8 # board size
|
||||
if (sr == "") { sr = 1 } # starting row
|
||||
if (sc == "") { sc = 1 } # starting column
|
||||
split("2 2 -2 -2 1 1 -1 -1",X," ")
|
||||
split("1 -1 1 -1 2 -2 2 -2",Y," ")
|
||||
printf("\n%dx%d board: starting row=%d col=%d\n",N,N,sr,sc)
|
||||
move(sr,sc,0)
|
||||
exit(1)
|
||||
}
|
||||
function move(x,y,m) {
|
||||
if (cantMove(x,y)) {
|
||||
return(0)
|
||||
}
|
||||
P[x,y] = ++m
|
||||
if (m == N ^ 2) {
|
||||
printBoard()
|
||||
exit(0)
|
||||
}
|
||||
tryBestMove(x,y,m)
|
||||
}
|
||||
function cantMove(x,y) {
|
||||
return( P[x,y] || x<1 || x>N || y<1 || y>N )
|
||||
}
|
||||
function tryBestMove(x,y,m, i) {
|
||||
i = bestMove(x,y)
|
||||
move(x+X[i],y+Y[i],m)
|
||||
}
|
||||
function bestMove(x,y, arg1,arg2,c,i,min,out) {
|
||||
# Warnsdorff's rule: go to where there are fewest next moves
|
||||
min = N ^ 2 + 1
|
||||
for (i in X) {
|
||||
arg1 = x + X[i]
|
||||
arg2 = y + Y[i]
|
||||
if (!cantMove(arg1,arg2)) {
|
||||
c = countNext(arg1,arg2)
|
||||
if (c < min) {
|
||||
min = c
|
||||
out = i
|
||||
}
|
||||
}
|
||||
}
|
||||
return(out)
|
||||
}
|
||||
function countNext(x,y, i,out) {
|
||||
for (i in X) {
|
||||
out += (!cantMove(x+X[i],y+Y[i]))
|
||||
}
|
||||
return(out)
|
||||
}
|
||||
function printBoard( i,j,leng) {
|
||||
leng = length(N*N)
|
||||
for (i=1; i<=N; i++) {
|
||||
for (j=1; j<=N; j++) {
|
||||
printf(" %*d",leng,P[i,j])
|
||||
}
|
||||
printf("\n")
|
||||
}
|
||||
}
|
||||
22
Task/Knights-tour/Ada/knights-tour-1.ada
Normal file
22
Task/Knights-tour/Ada/knights-tour-1.ada
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
generic
|
||||
Size: Integer;
|
||||
package Knights_Tour is
|
||||
|
||||
subtype Index is Integer range 1 .. Size;
|
||||
type Tour is array (Index, Index) of Natural;
|
||||
Empty: Tour := (others => (others => 0));
|
||||
|
||||
function Get_Tour(Start_X, Start_Y: Index; Scene: Tour := Empty) return Tour;
|
||||
-- finds tour via backtracking
|
||||
-- either no tour has been found, i.e., Get_Tour returns Scene
|
||||
-- or the Result(X,Y)=K if and only if I,J is visited at the K-th move
|
||||
-- for all X, Y, Scene(X,Y) must be either 0 or Natural'Last,
|
||||
-- where Scene(X,Y)=Natural'Last means "don't visit coordiates (X,Y)!"
|
||||
|
||||
function Count_Moves(Board: Tour) return Natural;
|
||||
-- counts the number of possible moves, i.e., the number of 0's on the board
|
||||
|
||||
procedure Tour_IO(The_Tour: Tour; Width: Natural := 4);
|
||||
-- writes The_Tour to the output using Ada.Text_IO;
|
||||
|
||||
end Knights_Tour;
|
||||
85
Task/Knights-tour/Ada/knights-tour-2.ada
Normal file
85
Task/Knights-tour/Ada/knights-tour-2.ada
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
with Ada.Text_IO, Ada.Integer_Text_IO;
|
||||
|
||||
package body Knights_Tour is
|
||||
|
||||
|
||||
type Pair is array(1..2) of Integer;
|
||||
type Pair_Array is array (Positive range <>) of Pair;
|
||||
|
||||
Pairs: constant Pair_Array (1..8)
|
||||
:= ((-2,1),(-1,2),(1,2),(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1));
|
||||
-- places for the night to go (relative to the current position)
|
||||
|
||||
function Count_Moves(Board: Tour) return Natural is
|
||||
N: Natural := 0;
|
||||
begin
|
||||
for I in Index loop
|
||||
for J in Index loop
|
||||
if Board(I,J) < Natural'Last then
|
||||
N := N + 1;
|
||||
end if;
|
||||
end loop;
|
||||
end loop;
|
||||
return N;
|
||||
end Count_Moves;
|
||||
|
||||
function Get_Tour(Start_X, Start_Y: Index; Scene: Tour := Empty)
|
||||
return Tour is
|
||||
Done: Boolean;
|
||||
Move_Count: Natural := Count_Moves(Scene);
|
||||
Visited: Tour;
|
||||
|
||||
-- Visited(I, J) = 0: not yet visited
|
||||
-- Visited(I, J) = K: visited at the k-th move
|
||||
-- Visited(I, J) = Integer'Last: never visit
|
||||
|
||||
procedure Visit(X, Y: Index; Move_Number: Positive; Found: out Boolean) is
|
||||
XX, YY: Integer;
|
||||
begin
|
||||
Found := False;
|
||||
Visited(X, Y) := Move_Number;
|
||||
if Move_Number = Move_Count then
|
||||
Found := True;
|
||||
else
|
||||
for P in Pairs'Range loop
|
||||
XX := X + Pairs(P)(1);
|
||||
YY := Y + Pairs(P)(2);
|
||||
if (XX in Index) and then (YY in Index)
|
||||
and then Visited(XX, YY) = 0 then
|
||||
Visit(XX, YY, Move_Number+1, Found); -- recursion
|
||||
if Found then
|
||||
return; -- no need to search further
|
||||
end if;
|
||||
end if;
|
||||
end loop;
|
||||
Visited(X, Y) := 0; -- undo previous mark
|
||||
end if;
|
||||
end Visit;
|
||||
|
||||
begin
|
||||
Visited := Scene;
|
||||
Visit(Start_X, Start_Y, 1, Done);
|
||||
if not Done then
|
||||
Visited := Scene;
|
||||
end if;
|
||||
return Visited;
|
||||
end Get_Tour;
|
||||
|
||||
procedure Tour_IO(The_Tour: Tour; Width: Natural := 4) is
|
||||
begin
|
||||
for I in Index loop
|
||||
for J in Index loop
|
||||
if The_Tour(I, J) < Integer'Last then
|
||||
Ada.Integer_Text_IO.Put(The_Tour(I, J), Width);
|
||||
else
|
||||
for W in 1 .. Width-1 loop
|
||||
Ada.Text_IO.Put(" ");
|
||||
end loop;
|
||||
Ada.Text_IO.Put("-"); -- deliberately not visited
|
||||
end if;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
end loop;
|
||||
end Tour_IO;
|
||||
|
||||
end Knights_Tour;
|
||||
11
Task/Knights-tour/Ada/knights-tour-3.ada
Normal file
11
Task/Knights-tour/Ada/knights-tour-3.ada
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
with Knights_Tour, Ada.Command_Line;
|
||||
|
||||
procedure Test_Knight is
|
||||
|
||||
Size: Positive := Positive'Value(Ada.Command_Line.Argument(1));
|
||||
|
||||
package KT is new Knights_Tour(Size => Size);
|
||||
|
||||
begin
|
||||
KT.Tour_IO(KT.Get_Tour(1, 1));
|
||||
end Test_Knight;
|
||||
4
Task/Knights-tour/Ada/knights-tour-4.ada
Normal file
4
Task/Knights-tour/Ada/knights-tour-4.ada
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
function Warnsdorff_Get_Tour(Start_X, Start_Y: Index; Scene: Tour := Empty)
|
||||
return Tour;
|
||||
-- uses Warnsdorff heurisitic to find a tour faster
|
||||
-- same interface as Get_Tour
|
||||
86
Task/Knights-tour/Ada/knights-tour-5.ada
Normal file
86
Task/Knights-tour/Ada/knights-tour-5.ada
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
function Warnsdorff_Get_Tour(Start_X, Start_Y: Index; Scene: Tour := Empty)
|
||||
return Tour is
|
||||
Done: Boolean;
|
||||
Visited: Tour; -- see comments from Get_Tour above
|
||||
Move_Count: Natural := Count_Moves(Scene);
|
||||
|
||||
function Neighbors(X, Y: Index) return Natural is
|
||||
Result: Natural := 0;
|
||||
begin
|
||||
for P in Pairs'Range loop
|
||||
if X+Pairs(P)(1) in Index and then Y+Pairs(P)(2) in Index and then
|
||||
Visited(X+Pairs(P)(1), Y+Pairs(P)(2)) = 0 then
|
||||
Result := Result + 1;
|
||||
end if;
|
||||
end loop;
|
||||
return Result;
|
||||
end Neighbors;
|
||||
|
||||
procedure Sort(Options: in out Pair_Array) is
|
||||
N_Bors: array(Options'Range) of Natural;
|
||||
K: Positive range Options'Range;
|
||||
N: Natural;
|
||||
P: Pair;
|
||||
begin
|
||||
for Opt in Options'Range loop
|
||||
N_Bors(Opt) := Neighbors(Options(Opt)(1), Options(Opt)(2));
|
||||
end loop;
|
||||
for Opt in Options'Range loop
|
||||
K := Opt;
|
||||
for Alternative in Opt+1 .. Options'Last loop
|
||||
if N_Bors(Alternative) < N_Bors(Opt) then
|
||||
K := Alternative;
|
||||
end if;
|
||||
end loop;
|
||||
N := N_Bors(Opt);
|
||||
N_Bors(Opt) := N_Bors(K);
|
||||
N_Bors(K) := N;
|
||||
P := Options(Opt);
|
||||
Options(Opt) := Options(K);
|
||||
Options(K) := P;
|
||||
end loop;
|
||||
end Sort;
|
||||
|
||||
procedure Visit(X, Y: Index; Move: Positive; Found: out Boolean) is
|
||||
Next_Count: Natural range 0 .. 8 := 0;
|
||||
Next_Steps: Pair_Array(1 .. 8);
|
||||
XX, YY: Integer;
|
||||
begin
|
||||
Found := False;
|
||||
Visited(X, Y) := Move;
|
||||
if Move = Move_Count then
|
||||
Found := True;
|
||||
else
|
||||
-- consider all possible places to go
|
||||
for P in Pairs'Range loop
|
||||
XX := X + Pairs(P)(1);
|
||||
YY := Y + Pairs(P)(2);
|
||||
if (XX in Index) and then (YY in Index)
|
||||
and then Visited(XX, YY) = 0 then
|
||||
Next_Count := Next_Count+1;
|
||||
Next_Steps(Next_Count) := (XX, YY);
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
Sort(Next_Steps(1 .. Next_Count));
|
||||
|
||||
for N in 1 .. Next_Count loop
|
||||
Visit(Next_Steps(N)(1), Next_Steps(N)(2), Move+1, Found);
|
||||
if Found then
|
||||
return; -- no need to search further
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
-- if we didn't return above, we have to undo our move
|
||||
Visited(X, Y) := 0;
|
||||
end if;
|
||||
end Visit;
|
||||
|
||||
begin
|
||||
Visited := Scene;
|
||||
Visit(Start_X, Start_Y, 1, Done);
|
||||
if not Done then
|
||||
Visited := Scene;
|
||||
end if;
|
||||
return Visited;
|
||||
end Warnsdorff_Get_Tour;
|
||||
11
Task/Knights-tour/Ada/knights-tour-6.ada
Normal file
11
Task/Knights-tour/Ada/knights-tour-6.ada
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
with Knights_Tour, Ada.Command_Line;
|
||||
|
||||
procedure Test_Fast is
|
||||
|
||||
Size: Positive := Positive'Value(Ada.Command_Line.Argument(1));
|
||||
|
||||
package KT is new Knights_Tour(Size => Size);
|
||||
|
||||
begin
|
||||
KT.Tour_IO(KT.Warnsdorff_Get_Tour(1, 1));
|
||||
end Test_Fast;
|
||||
96
Task/Knights-tour/AutoHotkey/knights-tour.ahk
Normal file
96
Task/Knights-tour/AutoHotkey/knights-tour.ahk
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#SingleInstance, Force
|
||||
#NoEnv
|
||||
SetBatchLines, -1
|
||||
; Uncomment if Gdip.ahk is not in your standard library
|
||||
;#Include, Gdip.ahk
|
||||
If !pToken := Gdip_Startup(){
|
||||
MsgBox, 48, Gdiplus error!, Gdiplus failed to start. Please ensure you have Gdiplus on your system.
|
||||
ExitApp
|
||||
}
|
||||
; I've added a simple new function here, just to ensure if anyone is having any problems then to make sure they are using the correct library version
|
||||
if (Gdip_LibraryVersion() < 1.30)
|
||||
{
|
||||
MsgBox, 48, Version error!, Please download the latest version of the gdi+ library
|
||||
ExitApp
|
||||
}
|
||||
OnExit, Exit
|
||||
tour := "a1 b3 d2 c4 a5 b7 d8 e6 d4 b5 c7 a8 b6 c8 a7 c6 b8 a6 b4 d5 e3 d1 b2 a4 c5 d7 f8 h7 f6 g8 h6 f7 h8 g6 e7 f5 h4 g2 e1 d3 e5 g4 f2 h1 g3 f1 h2 f3 g1 h3 g5 e4 d6 e8 g7 h5 f4 e2 c1 a2 c3 b1 a3 c2 "
|
||||
; Knight's tour with maximum symmetry by George Jelliss, http://www.mayhematics.com/t/8f.htm
|
||||
; I know, I know, but I followed the task outline to the letter! Besides, this path is the prettiest.
|
||||
|
||||
; Input: starting square
|
||||
InputBox, start, Knight's Tour Start, Enter Knight's starting location in algebraic notation:, , , , , , , , b3
|
||||
i := InStr(tour, start)
|
||||
If i=0
|
||||
{
|
||||
Msgbox Error, please try again.
|
||||
Reload
|
||||
}
|
||||
; Output: move sequence
|
||||
Msgbox % tour := SubStr(tour, i) . SubStr(tour, 1, i-1)
|
||||
|
||||
; Animation
|
||||
tour .= SubStr(tour, 1, 3)
|
||||
, CellSize := 30 ; pixels
|
||||
, Width := Height := 9*CellSize
|
||||
, TopLeftX := (A_ScreenWidth - Width) // 2
|
||||
, TopLeftY := (A_ScreenHeight - Height) // 2
|
||||
Gui, -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
|
||||
Gui, Show, NA ; show board (currently transparent)
|
||||
hwnd1 := WinExist() ; required for Gdip
|
||||
OnMessage(0x201, "WM_LBUTTONDOWN")
|
||||
, hbm := CreateDIBSection(Width, Height)
|
||||
, hdc := CreateCompatibleDC()
|
||||
, obm := SelectObject(hdc, hbm)
|
||||
, G := Gdip_GraphicsFromHDC(hdc)
|
||||
, Gdip_SetSmoothingMode(G, 4)
|
||||
|
||||
Loop 1 ; remove '1' and uncomment next line to loop infinitely
|
||||
{
|
||||
;Gdip_GraphicsClear(G) ; uncomment to loop infinitely
|
||||
cOdd := "0xFFFFCE9E" ; create brushes
|
||||
, cEven := "0xFFD18B47"
|
||||
, pBrushOdd := Gdip_BrushCreateSolid(cOdd)
|
||||
, pBrushEven := Gdip_BrushCreateSolid(cEven)
|
||||
|
||||
Loop 64 ; layout board
|
||||
{
|
||||
Row := mod(A_Index-1,8)+1
|
||||
, Col := (A_Index-1)//8+1
|
||||
, Gdip_FillRectangle(G, mod(Row+Col,2) ? pBrushOdd : pBrushEven, Col * CellSize + 1, Row * CellSize + 1, CellSize - 2, CellSize - 2)
|
||||
}
|
||||
Gdip_DeleteBrush(pBrushOdd) ; cleanup memory
|
||||
, Gdip_DeleteBrush(pBrushEven)
|
||||
, UpdateLayeredWindow(hwnd1, hdc, TopLeftX, TopLeftY, Width, Height) ; update board
|
||||
|
||||
, pPen := Gdip_CreatePen(0x66FF0000, CellSize/10) ; create pen
|
||||
, Algebraic := SubStr(tour,1,2) ; get starting coordinates
|
||||
, x := (Asc(SubStr(Algebraic, 1, 1))-96+0.5)*CellSize
|
||||
, y := (9.5-SubStr(Algebraic, 2, 1))*CellSize
|
||||
|
||||
Loop 64 ; trace path
|
||||
{
|
||||
Sleep, 0.5*1000
|
||||
xold := x, yold := y ; a line has start and end points
|
||||
, Algebraic := SubStr(tour,(A_Index)*3+1,2) ; get new coordinates
|
||||
, x := (Asc(SubStr(Algebraic, 1, 1))-96+0.5)*CellSize
|
||||
, y := (9.5-SubStr(Algebraic, 2, 1))*CellSize
|
||||
, Gdip_DrawLine(G, pPen, xold, yold, x, y)
|
||||
, UpdateLayeredWindow(hwnd1, hdc, TopLeftX, TopLeftY, Width, Height) ; update board
|
||||
}
|
||||
Gdip_DeletePen(pPen)
|
||||
}
|
||||
Return
|
||||
|
||||
GuiEscape:
|
||||
ExitApp
|
||||
|
||||
Exit:
|
||||
Gdip_Shutdown(pToken)
|
||||
ExitApp
|
||||
|
||||
WM_LBUTTONDOWN()
|
||||
{
|
||||
If (A_Gui = 1)
|
||||
PostMessage, 0xA1, 2
|
||||
}
|
||||
60
Task/Knights-tour/BBC-BASIC/knights-tour.basic
Normal file
60
Task/Knights-tour/BBC-BASIC/knights-tour.basic
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
VDU 23,22,256;256;16,16,16,128
|
||||
VDU 23,23,4;0;0;0;
|
||||
OFF
|
||||
GCOL 4,15
|
||||
FOR x% = 0 TO 512-128 STEP 128
|
||||
RECTANGLE FILL x%,0,64,512
|
||||
NEXT
|
||||
FOR y% = 0 TO 512-128 STEP 128
|
||||
RECTANGLE FILL 0,y%,512,64
|
||||
NEXT
|
||||
GCOL 9
|
||||
|
||||
DIM Board%(7,7)
|
||||
X% = 0
|
||||
Y% = 0
|
||||
Total% = 0
|
||||
REPEAT
|
||||
Board%(X%,Y%) = TRUE
|
||||
IF Total% DRAW X%*64+32,Y%*64+32 ELSE MOVE X%*64+32,Y%*64+32
|
||||
Total% += 1
|
||||
UNTIL NOT FNchoosemove(X%, Y%)
|
||||
IF Total%<>64 STOP
|
||||
REPEAT WAIT 1 : UNTIL FALSE
|
||||
END
|
||||
|
||||
DEF FNchoosemove(RETURN X%, RETURN Y%)
|
||||
LOCAL M%, newx%, newy%
|
||||
M% = 9
|
||||
PROCtrymove(X%+1, Y%+2, M%, newx%, newy%)
|
||||
PROCtrymove(X%+1, Y%-2, M%, newx%, newy%)
|
||||
PROCtrymove(X%-1, Y%+2, M%, newx%, newy%)
|
||||
PROCtrymove(X%-1, Y%-2, M%, newx%, newy%)
|
||||
PROCtrymove(X%+2, Y%+1, M%, newx%, newy%)
|
||||
PROCtrymove(X%+2, Y%-1, M%, newx%, newy%)
|
||||
PROCtrymove(X%-2, Y%+1, M%, newx%, newy%)
|
||||
PROCtrymove(X%-2, Y%-1, M%, newx%, newy%)
|
||||
IF M%=9 THEN = FALSE
|
||||
X% = newx% : Y% = newy%
|
||||
= TRUE
|
||||
|
||||
DEF PROCtrymove(X%, Y%, RETURN M%, RETURN newx%, RETURN newy%)
|
||||
LOCAL N%
|
||||
IF NOT FNvalidmove(X%,Y%) THEN ENDPROC
|
||||
IF FNvalidmove(X%+1,Y%+2) N% += 1
|
||||
IF FNvalidmove(X%+1,Y%-2) N% += 1
|
||||
IF FNvalidmove(X%-1,Y%+2) N% += 1
|
||||
IF FNvalidmove(X%-1,Y%-2) N% += 1
|
||||
IF FNvalidmove(X%+2,Y%+1) N% += 1
|
||||
IF FNvalidmove(X%+2,Y%-1) N% += 1
|
||||
IF FNvalidmove(X%-2,Y%+1) N% += 1
|
||||
IF FNvalidmove(X%-2,Y%-1) N% += 1
|
||||
IF N%>M% THEN ENDPROC
|
||||
IF N%=M% IF RND(2)=1 THEN ENDPROC
|
||||
M% = N%
|
||||
newx% = X% : newy% = Y%
|
||||
ENDPROC
|
||||
|
||||
DEF FNvalidmove(X%,Y%)
|
||||
IF X%<0 OR X%>7 OR Y%<0 OR Y%>7 THEN = FALSE
|
||||
= NOT(Board%(X%,Y%))
|
||||
105
Task/Knights-tour/Bracmat/knights-tour.bracmat
Normal file
105
Task/Knights-tour/Bracmat/knights-tour.bracmat
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
( knightsTour
|
||||
= validmoves WarnsdorffSort algebraicNotation init solve
|
||||
, x y fieldsToVisit
|
||||
. ~
|
||||
| ( validmoves
|
||||
= x y jumps moves
|
||||
. !arg:(?x.?y)
|
||||
& :?moves
|
||||
& ( jumps
|
||||
= dx dy Fs fxs fys fx fy
|
||||
. !arg:(?dx.?dy)
|
||||
& 1 -1:?Fs
|
||||
& !Fs:?fxs
|
||||
& whl
|
||||
' ( !fxs:%?fx ?fxs
|
||||
& !Fs:?fys
|
||||
& whl
|
||||
' ( !fys:%?fy ?fys
|
||||
& ( (!x+!fx*!dx.!y+!fy*!dy)
|
||||
: (>0:<9.>0:<9)
|
||||
|
|
||||
)
|
||||
!moves
|
||||
: ?moves
|
||||
)
|
||||
)
|
||||
)
|
||||
& jumps$(1.2)
|
||||
& jumps$(2.1)
|
||||
& !moves
|
||||
)
|
||||
& ( init
|
||||
= fields x y
|
||||
. :?fields
|
||||
& 0:?x
|
||||
& whl
|
||||
' ( 1+!x:<9:?x
|
||||
& 0:?y
|
||||
& whl
|
||||
' ( 1+!y:<9:?y
|
||||
& (!x.!y) !fields:?fields
|
||||
)
|
||||
)
|
||||
& !fields
|
||||
)
|
||||
& init$:?fieldsToVisit
|
||||
& ( WarnsdorffSort
|
||||
= sum moves elm weightedTerms
|
||||
. ( weightedTerms
|
||||
= pos alts fieldsToVisit moves move weight
|
||||
. !arg:(%?pos ?alts.?fieldsToVisit)
|
||||
& ( !fieldsToVisit:!pos
|
||||
& (0.!pos)
|
||||
| !fieldsToVisit:? !pos ?
|
||||
& validmoves$!pos:?moves
|
||||
& 0:?weight
|
||||
& whl
|
||||
' ( !moves:%?move ?moves
|
||||
& ( !fieldsToVisit:? !move ?
|
||||
& !weight+1:?weight
|
||||
|
|
||||
)
|
||||
)
|
||||
& (!weight.!pos)
|
||||
| 0
|
||||
)
|
||||
+ weightedTerms$(!alts.!fieldsToVisit)
|
||||
| 0
|
||||
)
|
||||
& weightedTerms$!arg:?sum
|
||||
& :?moves
|
||||
& whl
|
||||
' ( !sum:(#.?elm)+?sum
|
||||
& !moves !elm:?moves
|
||||
)
|
||||
& !moves
|
||||
)
|
||||
& ( solve
|
||||
= pos alts fieldsToVisit A Z tailOfSolution
|
||||
. !arg:(%?pos ?alts.?fieldsToVisit)
|
||||
& ( !fieldsToVisit:?A !pos ?Z
|
||||
& ( !A !Z:&
|
||||
| solve
|
||||
$ ( WarnsdorffSort$(validmoves$!pos.!A !Z)
|
||||
. !A !Z
|
||||
)
|
||||
)
|
||||
| solve$(!alts.!fieldsToVisit)
|
||||
)
|
||||
: ?tailOfSolution
|
||||
& !pos !tailOfSolution
|
||||
)
|
||||
& ( algebraicNotation
|
||||
= x y
|
||||
. !arg:(?x.?y) ?arg
|
||||
& str$(chr$(asc$a+!x+-1) !y " ")
|
||||
algebraicNotation$!arg
|
||||
|
|
||||
)
|
||||
& @(!arg:?x #?y)
|
||||
& asc$!x+-1*asc$a+1:?x
|
||||
& str
|
||||
$ (algebraicNotation$(solve$((!x.!y).!fieldsToVisit)))
|
||||
)
|
||||
& out$(knightsTour$a1);
|
||||
144
Task/Knights-tour/C++/knights-tour.cpp
Normal file
144
Task/Knights-tour/C++/knights-tour.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
template<int N = 8>
|
||||
class Board
|
||||
{
|
||||
public:
|
||||
array<pair<int, int>, 8> moves;
|
||||
array<array<int, N>, N> data;
|
||||
|
||||
Board()
|
||||
{
|
||||
moves[0] = make_pair(2, 1);
|
||||
moves[1] = make_pair(1, 2);
|
||||
moves[2] = make_pair(-1, 2);
|
||||
moves[3] = make_pair(-2, 1);
|
||||
moves[4] = make_pair(-2, -1);
|
||||
moves[5] = make_pair(-1, -2);
|
||||
moves[6] = make_pair(1, -2);
|
||||
moves[7] = make_pair(2, -1);
|
||||
}
|
||||
|
||||
array<int, 8> sortMoves(int x, int y) const
|
||||
{
|
||||
array<tuple<int, int>, 8> counts;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
int dx = get<0>(moves[i]);
|
||||
int dy = get<1>(moves[i]);
|
||||
|
||||
int c = 0;
|
||||
for(int j = 0; j < 8; ++j)
|
||||
{
|
||||
int x2 = x + dx + get<0>(moves[j]);
|
||||
int y2 = y + dy + get<1>(moves[j]);
|
||||
|
||||
if (x2 < 0 || x2 >= N || y2 < 0 || y2 >= N)
|
||||
continue;
|
||||
if(data[y2][x2] != 0)
|
||||
continue;
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
counts[i] = make_tuple(c, i);
|
||||
}
|
||||
|
||||
// Shuffle to randomly break ties
|
||||
random_shuffle(counts.begin(), counts.end());
|
||||
|
||||
// Lexicographic sort
|
||||
sort(counts.begin(), counts.end());
|
||||
|
||||
array<int, 8> out;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
out[i] = get<1>(counts[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
void solve(string start)
|
||||
{
|
||||
for(int v = 0; v < N; ++v)
|
||||
for(int u = 0; u < N; ++u)
|
||||
data[v][u] = 0;
|
||||
|
||||
int x0 = start[0] - 'a';
|
||||
int y0 = N - (start[1] - '0');
|
||||
data[y0][x0] = 1;
|
||||
|
||||
array<tuple<int, int, int, array<int, 8>>, N*N> order;
|
||||
order[0] = make_tuple(x0, y0, 0, sortMoves(x0, y0));
|
||||
|
||||
int n = 0;
|
||||
while(n < N*N-1)
|
||||
{
|
||||
int x = get<0>(order[n]);
|
||||
int y = get<1>(order[n]);
|
||||
|
||||
bool ok = false;
|
||||
for(int i = get<2>(order[n]); i < 8; ++i)
|
||||
{
|
||||
int dx = moves[get<3>(order[n])[i]].first;
|
||||
int dy = moves[get<3>(order[n])[i]].second;
|
||||
|
||||
if(x+dx < 0 || x+dx >= N || y+dy < 0 || y+dy >= N)
|
||||
continue;
|
||||
if(data[y + dy][x + dx] != 0)
|
||||
continue;
|
||||
|
||||
get<2>(order[n]) = i + 1;
|
||||
++n;
|
||||
data[y+dy][x+dx] = n + 1;
|
||||
order[n] = make_tuple(x+dx, y+dy, 0, sortMoves(x+dx, y+dy));
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!ok) // Failed. Backtrack.
|
||||
{
|
||||
data[y][x] = 0;
|
||||
--n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int N>
|
||||
friend ostream& operator<<(ostream &out, const Board<N> &b);
|
||||
};
|
||||
|
||||
template<int N>
|
||||
ostream& operator<<(ostream &out, const Board<N> &b)
|
||||
{
|
||||
for (int v = 0; v < N; ++v)
|
||||
{
|
||||
for (int u = 0; u < N; ++u)
|
||||
{
|
||||
if (u != 0) out << ",";
|
||||
out << setw(3) << b.data[v][u];
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Board<5> b1;
|
||||
b1.solve("c3");
|
||||
cout << b1 << endl;
|
||||
|
||||
Board<8> b2;
|
||||
b2.solve("b5");
|
||||
cout << b2 << endl;
|
||||
|
||||
Board<31> b3; // Max size for <1000 squares
|
||||
b3.solve("a1");
|
||||
cout << b3 << endl;
|
||||
return 0;
|
||||
}
|
||||
84
Task/Knights-tour/C-sharp/knights-tour.cs
Normal file
84
Task/Knights-tour/C-sharp/knights-tour.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace prog
|
||||
{
|
||||
class MainClass
|
||||
{
|
||||
const int N = 8;
|
||||
|
||||
readonly static int[,] moves = { {+1,-2},{+2,-1},{+2,+1},{+1,+2},
|
||||
{-1,+2},{-2,+1},{-2,-1},{-1,-2} };
|
||||
struct ListMoves
|
||||
{
|
||||
public int x, y;
|
||||
public ListMoves( int _x, int _y ) { x = _x; y = _y; }
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
int[,] board = new int[N,N];
|
||||
board.Initialize();
|
||||
|
||||
int x = 0, // starting position
|
||||
y = 0;
|
||||
|
||||
List<ListMoves> list = new List<ListMoves>(N*N);
|
||||
list.Add( new ListMoves(x,y) );
|
||||
|
||||
do
|
||||
{
|
||||
if ( Move_Possible( board, x, y ) )
|
||||
{
|
||||
int move = board[x,y];
|
||||
board[x,y]++;
|
||||
x += moves[move,0];
|
||||
y += moves[move,1];
|
||||
list.Add( new ListMoves(x,y) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( board[x,y] >= 8 )
|
||||
{
|
||||
board[x,y] = 0;
|
||||
list.RemoveAt(list.Count-1);
|
||||
if ( list.Count == 0 )
|
||||
{
|
||||
Console.WriteLine( "No solution found." );
|
||||
return;
|
||||
}
|
||||
x = list[list.Count-1].x;
|
||||
y = list[list.Count-1].y;
|
||||
}
|
||||
board[x,y]++;
|
||||
}
|
||||
}
|
||||
while( list.Count < N*N );
|
||||
|
||||
int last_x = list[0].x,
|
||||
last_y = list[0].y;
|
||||
string letters = "ABCDEFGH";
|
||||
for( int i=1; i<list.Count; i++ )
|
||||
{
|
||||
Console.WriteLine( string.Format("{0,2}: ", i) + letters[last_x] + (last_y+1) + " - " + letters[list[i].x] + (list[i].y+1) );
|
||||
|
||||
last_x = list[i].x;
|
||||
last_y = list[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
static bool Move_Possible( int[,] board, int cur_x, int cur_y )
|
||||
{
|
||||
if ( board[cur_x,cur_y] >= 8 )
|
||||
return false;
|
||||
|
||||
int new_x = cur_x + moves[board[cur_x,cur_y],0],
|
||||
new_y = cur_y + moves[board[cur_x,cur_y],1];
|
||||
|
||||
if ( new_x >= 0 && new_x < N && new_y >= 0 && new_y < N && board[new_x,new_y] == 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Task/Knights-tour/C/knights-tour.c
Normal file
103
Task/Knights-tour/C/knights-tour.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef unsigned char cell;
|
||||
int dx[] = { -2, -2, -1, 1, 2, 2, 1, -1 };
|
||||
int dy[] = { -1, 1, 2, 2, 1, -1, -2, -2 };
|
||||
|
||||
void init_board(int w, int h, cell **a, cell **b)
|
||||
{
|
||||
int i, j, k, x, y, p = w + 4, q = h + 4;
|
||||
/* b is board; a is board with 2 rows padded at each side */
|
||||
a[0] = (cell*)(a + q);
|
||||
b[0] = a[0] + 2;
|
||||
|
||||
for (i = 1; i < q; i++) {
|
||||
a[i] = a[i-1] + p;
|
||||
b[i] = a[i] + 2;
|
||||
}
|
||||
|
||||
memset(a[0], 255, p * q);
|
||||
for (i = 0; i < h; i++) {
|
||||
for (j = 0; j < w; j++) {
|
||||
for (k = 0; k < 8; k++) {
|
||||
x = j + dx[k], y = i + dy[k];
|
||||
if (b[i+2][j] == 255) b[i+2][j] = 0;
|
||||
b[i+2][j] += x >= 0 && x < w && y >= 0 && y < h;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define E "\033["
|
||||
int walk_board(int w, int h, int x, int y, cell **b)
|
||||
{
|
||||
int i, nx, ny, least;
|
||||
int steps = 0;
|
||||
printf(E"H"E"J"E"%d;%dH"E"32m[]"E"m", y + 1, 1 + 2 * x);
|
||||
|
||||
while (1) {
|
||||
/* occupy cell */
|
||||
b[y][x] = 255;
|
||||
|
||||
/* reduce all neighbors' neighbor count */
|
||||
for (i = 0; i < 8; i++)
|
||||
b[ y + dy[i] ][ x + dx[i] ]--;
|
||||
|
||||
/* find neighbor with lowest neighbor count */
|
||||
least = 255;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (b[ y + dy[i] ][ x + dx[i] ] < least) {
|
||||
nx = x + dx[i];
|
||||
ny = y + dy[i];
|
||||
least = b[ny][nx];
|
||||
}
|
||||
}
|
||||
|
||||
if (least > 7) {
|
||||
printf(E"%dH", h + 2);
|
||||
return steps == w * h - 1;
|
||||
}
|
||||
|
||||
if (steps++) printf(E"%d;%dH[]", y + 1, 1 + 2 * x);
|
||||
x = nx, y = ny;
|
||||
printf(E"%d;%dH"E"31m[]"E"m", y + 1, 1 + 2 * x);
|
||||
fflush(stdout);
|
||||
usleep(120000);
|
||||
}
|
||||
}
|
||||
|
||||
int solve(int w, int h)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
cell **a, **b;
|
||||
a = malloc((w + 4) * (h + 4) + sizeof(cell*) * (h + 4));
|
||||
b = malloc((h + 4) * sizeof(cell*));
|
||||
|
||||
while (1) {
|
||||
init_board(w, h, a, b);
|
||||
if (walk_board(w, h, x, y, b + 2)) {
|
||||
printf("Success!\n");
|
||||
return 1;
|
||||
}
|
||||
if (++x >= w) x = 0, y++;
|
||||
if (y >= h) {
|
||||
printf("Failed to find a solution\n");
|
||||
return 0;
|
||||
}
|
||||
printf("Any key to try next start position");
|
||||
getchar();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int c, char **v)
|
||||
{
|
||||
int w, h;
|
||||
if (c < 2 || (w = atoi(v[1])) <= 0) w = 8;
|
||||
if (c < 3 || (h = atoi(v[2])) <= 0) h = w;
|
||||
solve(w, h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
Task/Knights-tour/Clojure/knights-tour.clj
Normal file
25
Task/Knights-tour/Clojure/knights-tour.clj
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
(defn isin? [x li]
|
||||
(not= [] (filter #(= x %) li)))
|
||||
|
||||
(defn options [movements pmoves n]
|
||||
(let [x (first (last movements)) y (second (last movements))
|
||||
op (vec (map #(vector (+ x (first %)) (+ y (second %))) pmoves))
|
||||
vop (filter #(and (>= (first %) 0) (>= (last %) 0)) op)
|
||||
vop1 (filter #(and (< (first %) n) (< (last %) n)) vop)]
|
||||
(vec (filter #(not (isin? % movements)) vop1))))
|
||||
|
||||
(defn next-move [movements pmoves n]
|
||||
(let [op (options movements pmoves n)
|
||||
sp (map #(vector % (count (options (conj movements %) pmoves n))) op)
|
||||
m (apply min (map last sp))]
|
||||
(first (rand-nth (filter #(= m (last %)) sp)))))
|
||||
|
||||
(defn jumps [n pos]
|
||||
(let [movements (vector pos)
|
||||
pmoves [[1 2] [1 -2] [2 1] [2 -1]
|
||||
[-1 2] [-1 -2] [-2 -1] [-2 1]]]
|
||||
(loop [mov movements x 1]
|
||||
(if (= x (* n n))
|
||||
mov
|
||||
(let [np (next-move mov pmoves n)]
|
||||
(recur (conj mov np) (inc x)))))))
|
||||
114
Task/Knights-tour/CoffeeScript/knights-tour-1.coffee
Normal file
114
Task/Knights-tour/CoffeeScript/knights-tour-1.coffee
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
graph_tours = (graph, max_num_solutions) ->
|
||||
# graph is an array of arrays
|
||||
# graph[3] = [4, 5] means nodes 4 and 5 are reachable from node 3
|
||||
#
|
||||
# Returns an array of tours (up to max_num_solutions in size), where
|
||||
# each tour is an array of nodes visited in order, and where each
|
||||
# tour visits every node in the graph exactly once.
|
||||
#
|
||||
complete_tours = []
|
||||
visited = (false for node in graph)
|
||||
dead_ends = ({} for node in graph)
|
||||
tour = [0]
|
||||
|
||||
valid_neighbors = (i) ->
|
||||
arr = []
|
||||
for neighbor in graph[i]
|
||||
continue if visited[neighbor]
|
||||
continue if dead_ends[i][neighbor]
|
||||
arr.push neighbor
|
||||
arr
|
||||
|
||||
next_square_to_visit = (i) ->
|
||||
arr = valid_neighbors i
|
||||
return null if arr.length == 0
|
||||
|
||||
# We traverse to our neighbor who has the fewest neighbors itself.
|
||||
fewest_neighbors = valid_neighbors(arr[0]).length
|
||||
neighbor = arr[0]
|
||||
for i in [1...arr.length]
|
||||
n = valid_neighbors(arr[i]).length
|
||||
if n < fewest_neighbors
|
||||
fewest_neighbors = n
|
||||
neighbor = arr[i]
|
||||
neighbor
|
||||
|
||||
while tour.length > 0
|
||||
current_square = tour[tour.length - 1]
|
||||
visited[current_square] = true
|
||||
next_square = next_square_to_visit current_square
|
||||
if next_square?
|
||||
tour.push next_square
|
||||
if tour.length == graph.length
|
||||
complete_tours.push (n for n in tour) # clone
|
||||
break if complete_tours.length == max_num_solutions
|
||||
# pessimistically call this a dead end
|
||||
dead_ends[current_square][next_square] = true
|
||||
current_square = next_square
|
||||
else
|
||||
# we backtrack
|
||||
doomed_square = tour.pop()
|
||||
dead_ends[doomed_square] = {}
|
||||
visited[doomed_square] = false
|
||||
complete_tours
|
||||
|
||||
|
||||
knight_graph = (board_width) ->
|
||||
# Turn the Knight's Tour into a pure graph-traversal problem
|
||||
# by precomputing all the legal moves. Returns an array of arrays,
|
||||
# where each element in any subarray is the index of a reachable node.
|
||||
index = (i, j) ->
|
||||
# index squares from 0 to n*n - 1
|
||||
board_width * i + j
|
||||
|
||||
reachable_squares = (i, j) ->
|
||||
deltas = [
|
||||
[ 1, 2]
|
||||
[ 1, -2]
|
||||
[ 2, 1]
|
||||
[ 2, -1]
|
||||
[-1, 2]
|
||||
[-1, -2]
|
||||
[-2, 1]
|
||||
[-2, -1]
|
||||
]
|
||||
neighbors = []
|
||||
for delta in deltas
|
||||
[di, dj] = delta
|
||||
ii = i + di
|
||||
jj = j + dj
|
||||
if 0 <= ii < board_width
|
||||
if 0 <= jj < board_width
|
||||
neighbors.push index(ii, jj)
|
||||
neighbors
|
||||
|
||||
graph = []
|
||||
for i in [0...board_width]
|
||||
for j in [0...board_width]
|
||||
graph[index(i, j)] = reachable_squares i, j
|
||||
graph
|
||||
|
||||
illustrate_knights_tour = (tour, board_width) ->
|
||||
pad = (n) ->
|
||||
return " _" if !n?
|
||||
return " " + n if n < 10
|
||||
"#{n}"
|
||||
|
||||
console.log "\n------"
|
||||
moves = {}
|
||||
for square, i in tour
|
||||
moves[square] = i + 1
|
||||
for i in [0...board_width]
|
||||
s = ''
|
||||
for j in [0...board_width]
|
||||
s += " " + pad moves[i*board_width + j]
|
||||
console.log s
|
||||
|
||||
BOARD_WIDTH = 8
|
||||
MAX_NUM_SOLUTIONS = 100000
|
||||
|
||||
graph = knight_graph BOARD_WIDTH
|
||||
tours = graph_tours graph, MAX_NUM_SOLUTIONS
|
||||
console.log "#{tours.length} tours found (showing first and last)"
|
||||
illustrate_knights_tour tours[0], BOARD_WIDTH
|
||||
illustrate_knights_tour tours.pop(), BOARD_WIDTH
|
||||
26
Task/Knights-tour/CoffeeScript/knights-tour-2.coffee
Normal file
26
Task/Knights-tour/CoffeeScript/knights-tour-2.coffee
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
> time coffee knight.coffee
|
||||
100000 tours found (showing first and last)
|
||||
|
||||
------
|
||||
1 4 57 20 47 6 49 22
|
||||
34 19 2 5 58 21 46 7
|
||||
3 56 35 60 37 48 23 50
|
||||
18 33 38 55 52 59 8 45
|
||||
39 14 53 36 61 44 51 24
|
||||
32 17 40 43 54 27 62 9
|
||||
13 42 15 30 11 64 25 28
|
||||
16 31 12 41 26 29 10 63
|
||||
|
||||
------
|
||||
1 4 41 20 63 6 61 22
|
||||
34 19 2 5 42 21 44 7
|
||||
3 40 35 64 37 62 23 60
|
||||
18 33 38 47 56 43 8 45
|
||||
39 14 57 36 49 46 59 24
|
||||
32 17 48 55 58 27 50 9
|
||||
13 54 15 30 11 52 25 28
|
||||
16 31 12 53 26 29 10 51
|
||||
|
||||
real 0m29.741s
|
||||
user 0m25.656s
|
||||
sys 0m0.253s
|
||||
188
Task/Knights-tour/Common-Lisp/knights-tour.lisp
Normal file
188
Task/Knights-tour/Common-Lisp/knights-tour.lisp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Solving the knight's tour. ;;;
|
||||
;;; Warnsdorff's rule with random tie break. ;;;
|
||||
;;; Optionally outputs a closed tour. ;;;
|
||||
;;; Options from interactive prompt. ;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defparameter *side* 8)
|
||||
|
||||
(defun generate-chessboard (n)
|
||||
(loop for i below n append
|
||||
(loop for j below n collect (complex i j))))
|
||||
|
||||
(defparameter *chessboard*
|
||||
(generate-chessboard *side*))
|
||||
|
||||
(defun complex->algebraic (n)
|
||||
;; returns a string like "b2"
|
||||
(concatenate 'string
|
||||
;; 'a' is char #97: add it to the offset
|
||||
(string (character (+ 97 (realpart n))))
|
||||
;; indices start at 0, but algebraic starts at 1
|
||||
(string (digit-char (+ 1 (imagpart n))))))
|
||||
|
||||
(defun algebraic->complex (string)
|
||||
;; takes a string like "e4"
|
||||
(let ((row (char string 0))
|
||||
(col (char string 1)))
|
||||
(complex (- (char-code row) 97)
|
||||
(- (digit-char-p col) 1))))
|
||||
|
||||
(defconstant *knight-directions*
|
||||
(list
|
||||
(complex 1 2)
|
||||
(complex 2 1)
|
||||
(complex 1 -2)
|
||||
(complex 2 -1)
|
||||
(complex -1 2)
|
||||
(complex -2 1)
|
||||
(complex -1 -2)
|
||||
(complex -2 -1)))
|
||||
|
||||
(defun find-legal-moves (moves-list)
|
||||
;; 2. the move must not be on a case already visited
|
||||
(remove-if (lambda (m) (member m moves-list))
|
||||
;; 1. the move must be within the chessboard
|
||||
(intersection
|
||||
(mapcar (lambda (i) (+ (car moves-list) i)) *knight-directions*)
|
||||
*chessboard*)))
|
||||
|
||||
|
||||
;; Select between two moves by Warnsdorff's rule:
|
||||
;; pick the one with the lowest index or else
|
||||
;; randomly break the tie.
|
||||
;; Takes a cons in the form (n . #C(x y)).
|
||||
;; This will be the sorting rule for picking the next move.
|
||||
(defun w-rule (a b)
|
||||
(cond ((< (car a) (car b)) t)
|
||||
((> (car a) (car b)) nil)
|
||||
((= (car a) (car b))
|
||||
(zerop (random 2)))))
|
||||
|
||||
;; For every legal move in a given position,
|
||||
;; look forward one move and return a cons
|
||||
;; in the form (n . #C(x y)) where n is
|
||||
;; how many next free moves follow the first move.
|
||||
(defun return-weighted-moves (moves)
|
||||
(let ((candidates (find-legal-moves moves)))
|
||||
(loop for mv in candidates collect
|
||||
(cons
|
||||
(list-length (find-legal-moves (cons mv moves)))
|
||||
mv))))
|
||||
|
||||
;; Given a list of weighted moves (as above),
|
||||
;; pick one according to the w-rule
|
||||
(defun pick-among-weighted-moves (moves)
|
||||
;; prune dead ends one move early
|
||||
(let ((possible-moves
|
||||
(remove-if (lambda(m) (zerop (car m))) moves)))
|
||||
(cdar (sort possible-moves #'w-rule))))
|
||||
|
||||
(defun make-move (moves-list)
|
||||
(let ((next-move
|
||||
(if (< (list-length moves-list) (1- (list-length *chessboard*)))
|
||||
(pick-among-weighted-moves (return-weighted-moves moves-list))
|
||||
(car (find-legal-moves moves-list)))))
|
||||
(cons next-move moves-list)))
|
||||
|
||||
(defun make-tour (moves-list)
|
||||
;; takes a list of moves as an argument
|
||||
(if (null (car moves-list)) ; last move not found: start over
|
||||
(make-tour (last moves-list))
|
||||
(if (= (list-length moves-list) (list-length *chessboard*))
|
||||
moves-list
|
||||
(make-tour (make-move moves-list)))))
|
||||
|
||||
(defun make-closed-tour (moves-list)
|
||||
(let ((tour (make-tour moves-list)))
|
||||
(if (tour-closed-p tour)
|
||||
tour
|
||||
(make-closed-tour moves-list))))
|
||||
|
||||
(defun tour-closed-p (tour)
|
||||
;; takes a full tour as an argument
|
||||
(let ((start (car (last tour)))
|
||||
(end (car tour)))
|
||||
;; is the first position a legal move, when
|
||||
;; viewed from the last move?
|
||||
(if (member start (find-legal-moves (list end))) ; find-legal-moves takes a list
|
||||
t nil)))
|
||||
|
||||
(defun print-tour-linear (tour)
|
||||
;; takes a tour (moves list) with the last move first
|
||||
;; and prints it nicely in algebraic notation
|
||||
(let ((moves (mapcar #'complex->algebraic (reverse tour))))
|
||||
(format t "~{~A~^ -> ~}" moves)))
|
||||
|
||||
(defun tour->matrix (tour)
|
||||
;; takes a tour and makes a row-by-row 2D matrix
|
||||
;; from top to bottom (for further formatting & printing)
|
||||
(flet ((index-tour (tour) ; 1st local function
|
||||
(loop for i below (length tour)
|
||||
;; starting from index 1, not 0, so add 1;
|
||||
;; reverse because the last move is still in the car
|
||||
collect (cons (nth i (reverse tour)) (1+ i))))
|
||||
(get-row (n tour) ; 2nd local function
|
||||
;; in every row, the imaginary part (vertical offset) stays the same
|
||||
(remove-if-not (lambda (e) (= n (imagpart (car e)))) tour)))
|
||||
(let* ((indexed-tour (index-tour tour))
|
||||
(ordered-indexed-tour
|
||||
;; make a list of ordered rows
|
||||
(loop for i from (1- *side*) downto 0 collect
|
||||
(sort (get-row i indexed-tour)
|
||||
(lambda (a b) (< (realpart (car a)) (realpart (car b))))))))
|
||||
;; clean up, leaving only the indices
|
||||
(mapcar (lambda (e) (mapcar #'cdr e)) ordered-indexed-tour))))
|
||||
|
||||
(defun print-tour-matrix (tour)
|
||||
(mapcar (lambda (row)
|
||||
(format t "~{~3d~}~&" row)) (tour->matrix tour)))
|
||||
|
||||
;;; Handling options
|
||||
|
||||
(defstruct options
|
||||
closed
|
||||
start
|
||||
grid)
|
||||
|
||||
(defparameter *opts* (make-options))
|
||||
|
||||
;;; Interactive part
|
||||
|
||||
(defun prompt()
|
||||
(format t "Starting case (leave blank for random)? ")
|
||||
(let ((start (string (read-line))))
|
||||
(if (member start (mapcar #'complex->algebraic *chessboard*) :test #'equal)
|
||||
(setf (options-start *opts*) start))
|
||||
(format t "Require a closed tour (yes or default to no)? ")
|
||||
(let ((closed (read-line)))
|
||||
(if (or (equal closed "y") (equal closed "yes"))
|
||||
(setf (options-closed *opts*) t)))))
|
||||
|
||||
(defun main ()
|
||||
(let* ((start
|
||||
(if (options-start *opts*)
|
||||
(algebraic->complex (options-start *opts*))
|
||||
(complex (random *side*) (random *side*))))
|
||||
(closed (options-closed *opts*))
|
||||
(tour
|
||||
(if closed
|
||||
(make-closed-tour (list start))
|
||||
(make-tour (list start)))))
|
||||
(fresh-line)
|
||||
(if closed (princ "Closed "))
|
||||
(princ "Knight's tour")
|
||||
(if (options-start *opts*)
|
||||
(princ ":")
|
||||
(princ " (starting on a random case):"))
|
||||
(fresh-line)
|
||||
(print-tour-linear tour)
|
||||
(princ #\newline)
|
||||
(princ #\newline)
|
||||
(print-tour-matrix tour)))
|
||||
|
||||
;;; Good to go: invocation!
|
||||
|
||||
(prompt)
|
||||
(main)
|
||||
77
Task/Knights-tour/D/knights-tour-1.d
Normal file
77
Task/Knights-tour/D/knights-tour-1.d
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import std.stdio, std.algorithm, std.random, std.range,
|
||||
std.conv, std.typecons, std.typetuple;
|
||||
|
||||
int[N][N] knightTour(size_t N=8)(in string start)
|
||||
in {
|
||||
assert(start.length >= 2);
|
||||
} body {
|
||||
static struct P { int x, y; }
|
||||
|
||||
immutable P[8] moves = [P(2,1), P(1,2), P(-1,2), P(-2,1),
|
||||
P(-2,-1), P(-1,-2), P(1,-2), P(2,-1)];
|
||||
int[N][N] data;
|
||||
|
||||
int[8] sortMoves(in int x, in int y) {
|
||||
int[2][8] counts;
|
||||
foreach (immutable i, immutable ref d1; moves) {
|
||||
int c = 0;
|
||||
foreach (immutable ref d2; moves) {
|
||||
immutable p = P(x + d1.x + d2.x, y + d1.y + d2.y);
|
||||
if (p.x >= 0 && p.x < N && p.y >= 0 && p.y < N &&
|
||||
data[p.y][p.x] == 0)
|
||||
c++;
|
||||
}
|
||||
counts[i] = [c, i];
|
||||
}
|
||||
|
||||
counts[].randomShuffle; // Shuffle to randomly break ties.
|
||||
counts[].sort(); // Lexicographic sort.
|
||||
|
||||
int[8] result = void;
|
||||
transversal(counts[], 1).copy(result[]);
|
||||
return result;
|
||||
}
|
||||
|
||||
immutable p0 = P(start[0] - 'a', N - to!int(start[1 .. $]));
|
||||
data[p0.y][p0.x] = 1;
|
||||
|
||||
Tuple!(int, int, int, int[8])[N * N] order;
|
||||
order[0] = tuple(p0.x, p0.y, 0, sortMoves(p0.x, p0.y));
|
||||
|
||||
int n = 0;
|
||||
while (n < (N * N - 1)) {
|
||||
immutable int x = order[n][0];
|
||||
immutable int y = order[n][1];
|
||||
bool ok = false;
|
||||
foreach (immutable i; order[n][2] .. 8) {
|
||||
immutable P d = moves[order[n][3][i]];
|
||||
if (x+d.x < 0 || x+d.x >= N || y+d.y < 0 || y+d.y >= N)
|
||||
continue;
|
||||
|
||||
if (data[y + d.y][x + d.x] == 0) {
|
||||
order[n][2] = i + 1;
|
||||
n++;
|
||||
data[y + d.y][x + d.x] = n + 1;
|
||||
order[n] = tuple(x+d.x,y+d.y,0,sortMoves(x+d.x,y+d.y));
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) { // Failed. Backtrack.
|
||||
data[y][x] = 0;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void main() {
|
||||
foreach (immutable i, side; TypeTuple!(5, 8, 31, 101)) {
|
||||
immutable form = "%(%" ~ text(side ^^ 2).length.text ~ "d %)";
|
||||
foreach (ref row; ["c3", "b5", "a1", "a1"][i].knightTour!side)
|
||||
writefln(form, row);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
27
Task/Knights-tour/D/knights-tour-2.d
Normal file
27
Task/Knights-tour/D/knights-tour-2.d
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import std.stdio, std.math, std.algorithm, std.range, std.typecons;
|
||||
|
||||
alias Square = Tuple!(int,"x", int,"y");
|
||||
|
||||
const(Square)[] knightTour(in Square[] board, in Square[] moves) pure @safe nothrow {
|
||||
enum findMoves = (in Square sq) pure nothrow @safe =>
|
||||
cartesianProduct([1, -1, 2, -2], [1, -1, 2, -2])
|
||||
.filter!(ij => ij[0].abs != ij[1].abs)
|
||||
.map!(ij => Square(sq.x + ij[0], sq.y + ij[1]))
|
||||
.filter!(s => board.canFind(s) && !moves.canFind(s));
|
||||
auto newMoves = findMoves(moves.back);
|
||||
if (newMoves.empty)
|
||||
return moves;
|
||||
//alias warnsdorff = min!(s => findMoves(s).walkLength);
|
||||
//immutable newSq = newMoves.dropOne.fold!warnsdorff(newMoves.front);
|
||||
auto pairs = newMoves.map!(s => tuple(findMoves(s).walkLength, s));
|
||||
immutable newSq = reduce!min(pairs.front, pairs.dropOne)[1];
|
||||
return board.knightTour(moves ~ newSq);
|
||||
}
|
||||
|
||||
void main(in string[] args) {
|
||||
enum toSq = (in string xy) => Square(xy[0] - '`', xy[1] - '0');
|
||||
immutable toAlg = (in Square s) => [dchar(s.x + '`'), dchar(s.y + '0')];
|
||||
immutable sq = toSq((args.length == 2) ? args[1] : "e5");
|
||||
const board = iota(1, 9).cartesianProduct(iota(1, 9)).map!Square.array;
|
||||
writefln("%(%-(%s -> %)\n%)", board.knightTour([sq]).map!toAlg.chunks(8));
|
||||
}
|
||||
221
Task/Knights-tour/Delphi/knights-tour.delphi
Normal file
221
Task/Knights-tour/Delphi/knights-tour.delphi
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
{ These routines would normally be in a library,
|
||||
but are presented here for clarity }
|
||||
|
||||
function PointAdd(V1,V2: TPoint): TPoint;
|
||||
{Add V1 and V2}
|
||||
begin
|
||||
Result.X:= V1.X+V2.X;
|
||||
Result.Y:= V1.Y+V2.Y;
|
||||
end;
|
||||
|
||||
|
||||
const KnightMoves: array [0..7] of TPoint = (
|
||||
(X: 2; Y:1),(X: 2; Y:-1),
|
||||
(X:-2; Y:1),(X:-2; Y:-1),
|
||||
(X:1; Y: 2),(X:-1; Y: 2),
|
||||
(X:1; Y:-2),(X:-1; Y:-2));
|
||||
|
||||
var Board: array [0..7,0..7] of boolean;
|
||||
|
||||
var Path: array of TPoint;
|
||||
|
||||
var CellSize,BoardSize: integer;
|
||||
|
||||
var CurPos: TPoint;
|
||||
|
||||
var BestPath: integer;
|
||||
|
||||
{-------------------------------------------------------------}
|
||||
|
||||
procedure DrawBestPath(Image: TImage);
|
||||
begin
|
||||
Image.Canvas.TextOut(BoardSize+5,5, IntToStr(BestPath));
|
||||
end;
|
||||
|
||||
|
||||
procedure PushPath(P: TPoint);
|
||||
begin
|
||||
SetLength(Path,Length(Path)+1);
|
||||
Path[High(Path)]:=P;
|
||||
if Length(Path)>BestPath then BestPath:=Length(Path);
|
||||
end;
|
||||
|
||||
|
||||
function PopPath: TPoint;
|
||||
begin
|
||||
if Length(Path)<1 then exit;
|
||||
Result:=Path[High(Path)];
|
||||
SetLength(Path,Length(Path)-1);
|
||||
end;
|
||||
|
||||
|
||||
procedure ClearPath;
|
||||
begin
|
||||
SetLength(Path,0);
|
||||
end;
|
||||
|
||||
{-------- Routines to draw chess board and path --------------}
|
||||
|
||||
function GetCellCenter(P: TPoint): TPoint;
|
||||
{Get pixel position of the center of cell}
|
||||
begin
|
||||
Result.X:=CellSize div 2 + CellSize * P.X;
|
||||
Result.Y:=CellSize div 2 + CellSize * P.Y;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure DrawPoint(Canvas: TCanvas; P: TPoint);
|
||||
{Draw a point on the board}
|
||||
begin
|
||||
Canvas.Pen.Color:=clYellow;
|
||||
Canvas.MoveTo(P.X-1,P.Y-1);
|
||||
Canvas.LineTo(P.X+1,P.Y+1);
|
||||
Canvas.MoveTo(P.X+1,P.Y-1);
|
||||
Canvas.LineTo(P.X-1,P.Y+1);
|
||||
end;
|
||||
|
||||
|
||||
procedure DrawPathLine(Canvas: TCanvas; P1,P2: TPoint);
|
||||
{Draw the path line}
|
||||
var PS1,PS2: TPoint;
|
||||
begin
|
||||
PS1:=GetCellCenter(P1);
|
||||
PS2:=GetCellCenter(P2);
|
||||
Canvas.Pen.Width:=5;
|
||||
Canvas.Pen.Color:=clRed;
|
||||
Canvas.MoveTo(PS1.X,PS1.Y);
|
||||
Canvas.LineTo(PS2.X,PS2.Y);
|
||||
DrawPoint(Canvas,PS1);
|
||||
DrawPoint(Canvas,PS2);
|
||||
end;
|
||||
|
||||
|
||||
procedure DrawPath(Canvas: TCanvas);
|
||||
{Draw all points on the path}
|
||||
var I: integer;
|
||||
begin
|
||||
for I:=0 to High(Path)-1 do
|
||||
begin
|
||||
DrawPathLine(Canvas, Path[I],Path[I+1]);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure DrawBoard(Canvas: TCanvas);
|
||||
{Draw the chess board}
|
||||
var R,R2: TRect;
|
||||
var X,Y: integer;
|
||||
var Color: TColor;
|
||||
begin
|
||||
Canvas.Pen.Color:=clBlack;
|
||||
R:=Rect(0,0,BoardSize,BoardSize);
|
||||
Canvas.Rectangle(R);
|
||||
R:=Rect(0,0,CellSize,CellSize);
|
||||
for Y:=0 to High(Board[0]) do
|
||||
for X:=0 to High(Board) do
|
||||
begin
|
||||
R2:=R;
|
||||
if ((X+Y) mod 2)=0 then Color:=clWhite
|
||||
else Color:=clBlack;
|
||||
Canvas.Brush.Color:=Color;
|
||||
OffsetRect(R2,X * CellSize, Y * CellSize);
|
||||
Canvas.Rectangle(R2);
|
||||
end;
|
||||
DrawPath(Canvas);
|
||||
end;
|
||||
|
||||
|
||||
function AllVisited: boolean;
|
||||
{Test if all squares have been visit by path}
|
||||
var X,Y: integer;
|
||||
begin
|
||||
Result:=False;
|
||||
for Y:=0 to High(Board[0]) do
|
||||
for X:=0 to High(Board) do
|
||||
if not Board[X,Y] then exit;
|
||||
Result:=True;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure ClearBoard;
|
||||
{Clear all board positions}
|
||||
var X,Y: integer;
|
||||
begin
|
||||
for Y:=0 to High(Board[0]) do
|
||||
for X:=0 to High(Board) do
|
||||
Board[X,Y]:=False;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
function IsValidMove(Pos,Move: TPoint): boolean;
|
||||
{Test if potential move is valid}
|
||||
var NP: TPoint;
|
||||
begin
|
||||
Result:=False;
|
||||
NP:=PointAdd(Pos,Move);
|
||||
if (NP.X<0) or (NP.X>High(Board)) or
|
||||
(NP.Y<0) or (NP.Y>High(Board[0])) then exit;
|
||||
if Board[NP.X,NP.Y] then exit;
|
||||
Result:=True;
|
||||
end;
|
||||
|
||||
|
||||
procedure ConfigureScreen(Image: TImage);
|
||||
{Configure screen size}
|
||||
begin
|
||||
if Image.Width<Image.Height then BoardSize:=Image.Width
|
||||
else BoardSize:=Image.Height;
|
||||
CellSize:=BoardSize div 8;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
|
||||
procedure SetPosition(Image: TImage; P: TPoint; Value: boolean);
|
||||
{Set a new position by adding it to path}
|
||||
{Marking position as used and redrawing board}
|
||||
begin
|
||||
if Value then PushPath(P)
|
||||
else P:=PopPath;
|
||||
Board[P.X,P.Y]:=Value;
|
||||
DrawBoard(Image.Canvas);
|
||||
DrawBestPath(Image);
|
||||
Image.Repaint;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure TryAllMoves(Image: TImage; Pos: TPoint);
|
||||
{Recursively try all moves}
|
||||
var I: integer;
|
||||
var NewPos: TPoint;
|
||||
begin
|
||||
SetPosition(Image,Pos,True);
|
||||
if AllVisited then exit;
|
||||
for I:=0 to High(KnightMoves) do
|
||||
begin
|
||||
if AbortFlag then Exit;
|
||||
if IsValidMove(Pos,KnightMoves[I]) then
|
||||
begin
|
||||
NewPos:=PointAdd(Pos,KnightMoves[I]);
|
||||
TryAllMoves(Image,NewPos);
|
||||
end;
|
||||
end;
|
||||
SetPosition(Image,Pos,False);
|
||||
Application.ProcessMessages;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoKnightsTour(Image: TImage);
|
||||
{Solve Knights tour by testing all paths}
|
||||
begin
|
||||
BestPath:=0;
|
||||
ConfigureScreen(Image);
|
||||
ClearPath;
|
||||
ClearBoard;
|
||||
DrawBoard(Image.Canvas);
|
||||
TryAllMoves(Image, Point(0,0));
|
||||
end;
|
||||
209
Task/Knights-tour/ERRE/knights-tour.erre
Normal file
209
Task/Knights-tour/ERRE/knights-tour.erre
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
! **********************************************************************
|
||||
! * *
|
||||
! * IL GIRO DEL CAVALLO - come collocare un cavallo su di una *
|
||||
! * scacchiera n*n passando una sola volta *
|
||||
! * per ogni casella. *
|
||||
! * *
|
||||
! **********************************************************************
|
||||
! ----------------------------------------------------------------------
|
||||
! Inizializzazione dei parametri
|
||||
! ----------------------------------------------------------------------
|
||||
|
||||
PROGRAM KNIGHT
|
||||
|
||||
!$INTEGER
|
||||
!$KEY
|
||||
|
||||
DIM H[25,25],A[8],B[8],P0[8],P1[8]
|
||||
|
||||
!$INCLUDE="PC.LIB"
|
||||
|
||||
PROCEDURE INIT_SCACCHIERA
|
||||
! **********************************************************************
|
||||
! * Routine di inizializzazione scacchiera *
|
||||
! **********************************************************************
|
||||
FOR I1=1 TO 8 DO
|
||||
U=X+A[I1] V=Y+B[I1]
|
||||
IF (U>0 AND U<=N) AND (V>0 AND V<=N) THEN
|
||||
H[U,V]=H[U,V]-1
|
||||
END IF
|
||||
END FOR
|
||||
END PROCEDURE
|
||||
|
||||
PROCEDURE MOSTRA_SCACCHIERA
|
||||
! *********************************************************************
|
||||
! * Routine di visualizzazione della scacchiera *
|
||||
! *********************************************************************
|
||||
LOCATE(5,1) COLOR(0,7) PRINT(" Mossa num.";NMOS) COLOR(7,0)
|
||||
L2=N
|
||||
FOR I2=1 TO N DO
|
||||
PRINT
|
||||
FOR L1=1 TO N DO
|
||||
IF H[L1,L2]>0 THEN COLOR(15,0) END IF
|
||||
WRITE("####";H[L1,L2];)
|
||||
COLOR(7,0)
|
||||
END FOR
|
||||
L2=L2-1
|
||||
END FOR
|
||||
END PROCEDURE
|
||||
|
||||
PROCEDURE AGGIORNA_SCACCHIERA
|
||||
! *********************************************************************
|
||||
! * Routine di Aggiornamento Scacchiera *
|
||||
! *********************************************************************
|
||||
B=1
|
||||
FOR I1=1 TO 8 DO
|
||||
U=X+A[I1] V=Y+B[I1]
|
||||
IF (U>0 AND U<=N) AND (V>0 AND V<=N) THEN
|
||||
IF H[U,V]<=0 THEN
|
||||
H[U,V]=H[U,V]+1 B=0
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
IF B=1 THEN Q1=0 END IF
|
||||
END PROCEDURE
|
||||
|
||||
PROCEDURE MOSSA_MAX_PESO
|
||||
! *********************************************************************
|
||||
! * Cerca la prossima mossa con il massimo peso *
|
||||
! *********************************************************************
|
||||
M1=0 RO=1
|
||||
FOR W=1 TO 8 DO
|
||||
U=Z1+A[W] V=Z2+B[W]
|
||||
IF (U>0 AND U<=N) AND (V>0 AND V<=N) THEN
|
||||
IF H[U,V]<=0 AND H[U,V]<=M1 THEN
|
||||
IF H[U,V]=M1 THEN
|
||||
RO=RO+1 P0[RO]=W
|
||||
ELSE
|
||||
M1=H[U,V] Q1=1 T1=U T2=V RO=1 P0[1]=W
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
END PROCEDURE
|
||||
|
||||
PROCEDURE MOSSA_MIN_PESO
|
||||
! *********************************************************************
|
||||
! * Cerca la prossima mossa con il minimo peso *
|
||||
! *********************************************************************
|
||||
M1=-9 RO=1
|
||||
FOR W=1 TO 8 DO
|
||||
U=Z1+A[W] V=Z2+B[W]
|
||||
IF (U>0 AND U<=N) AND (V>0 AND V<=N) THEN
|
||||
IF H[U,V]<=0 AND H[U,V]>=M1 THEN
|
||||
IF H[U,V]=M1 THEN
|
||||
RO=RO+1 P0[RO]=W
|
||||
ELSE
|
||||
M1=H[U,V] Q1=1 T1=U T2=V RO=1 P0[1]=W
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
END PROCEDURE
|
||||
|
||||
BEGIN
|
||||
A[1]=1 A[2]=2 A[3]=2 A[4]=1
|
||||
A[5]=-1 A[6]=-2 A[7]=-2 A[8]=-1
|
||||
B[1]=2 B[2]=1 B[3]=-1 B[4]=-2
|
||||
B[5]=-2 B[6]=-1 B[7]=1 B[8]=2
|
||||
|
||||
CLS
|
||||
PRINT(" *** LA GALOPPATA DEL CAVALIERE ***")
|
||||
PRINT
|
||||
PRINT("Inserire la dimensione della scacchiera (max. 25)";)
|
||||
INPUT(N)
|
||||
PRINT("Inserire la caselle di partenza (x,y) ";)
|
||||
INPUT(X1,Y1)
|
||||
NMOS=1 A1=1 N1=N*N ESCAPE=FALSE
|
||||
! ----------------------------------------------------------------------
|
||||
! Set della scacchiera
|
||||
! ----------------------------------------------------------------------
|
||||
WHILE NOT ESCAPE DO
|
||||
FOR I=1 TO N DO
|
||||
FOR J=1 TO N DO
|
||||
H[I,J]=0
|
||||
END FOR
|
||||
END FOR
|
||||
FOR I=1 TO N DO
|
||||
FOR J=1 TO N DO
|
||||
X=I Y=J
|
||||
INIT_SCACCHIERA
|
||||
END FOR
|
||||
END FOR
|
||||
|
||||
! ----------------------------------------------------------------------
|
||||
! Effettua la prima mossa
|
||||
! ----------------------------------------------------------------------
|
||||
X=X1 Y=Y1 H[X,Y]=1 L=2
|
||||
AGGIORNA_SCACCHIERA
|
||||
Q1=1 Q2=1
|
||||
! -----------------------------------------------------------------------
|
||||
! Trova la prossima mossa
|
||||
! -----------------------------------------------------------------------
|
||||
WHILE Q1<>0 AND Q2<>0 DO
|
||||
Q1=0 Z1=X Z2=Y
|
||||
MOSSA_MIN_PESO
|
||||
IF RO<=1 THEN
|
||||
C1=T1 C2=T2
|
||||
ELSE
|
||||
! ------------------------------------------------------------------------
|
||||
! Esamina tutti i vincoli
|
||||
! ------------------------------------------------------------------------
|
||||
FOR K=1 TO RO DO
|
||||
P1[K]=P0[K]
|
||||
END FOR
|
||||
R1=RO
|
||||
IF A1=1 THEN M2=-9 ELSE M2=0 END IF
|
||||
FOR K=1 TO R1 DO
|
||||
F1=P1[K] Z1=X+A[F1] Z2=Y+B[F1]
|
||||
IF A1=1 THEN
|
||||
MOSSA_MAX_PESO
|
||||
IF M1<=M2 THEN
|
||||
!$NULL
|
||||
ELSE
|
||||
M2=M1 C1=Z1 C2=Z2
|
||||
END IF
|
||||
ELSE
|
||||
MOSSA_MIN_PESO
|
||||
IF M1>=M2 THEN
|
||||
!$NULL
|
||||
ELSE
|
||||
M2=M1 C1=Z1 C2=Z2
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
! ------------------------------------------------------------------------
|
||||
! Prossima mossa trovata:aggiorna la scacchiera
|
||||
! ------------------------------------------------------------------------
|
||||
END IF
|
||||
IF Q1<>0 THEN
|
||||
X=C1 Y=C2 H[X,Y]=L
|
||||
AGGIORNA_SCACCHIERA
|
||||
IF L=N1 THEN Q2=0 END IF
|
||||
END IF
|
||||
L=L+1
|
||||
MOSTRA_SCACCHIERA
|
||||
NMOS=NMOS+1
|
||||
END WHILE
|
||||
! ------------------------------------------------------------------------
|
||||
! La ricerca è terminata: visualizza i risultati
|
||||
! ------------------------------------------------------------------------
|
||||
PRINT PRINT
|
||||
IF Q2<>1 THEN
|
||||
PRINT("*** Trovata la soluzione! ***")
|
||||
MOSTRA_SCACCHIERA
|
||||
ESCAPE=TRUE
|
||||
ELSE
|
||||
IF A1=0 THEN
|
||||
PRINT("Nessuna soluzione.")
|
||||
ESCAPE=TRUE
|
||||
ELSE
|
||||
BEEP
|
||||
A1=0
|
||||
END IF
|
||||
END IF
|
||||
END WHILE
|
||||
REPEAT
|
||||
GET(A$)
|
||||
UNTIL A$<>""
|
||||
END PROGRAM
|
||||
68
Task/Knights-tour/EchoLisp/knights-tour-1.l
Normal file
68
Task/Knights-tour/EchoLisp/knights-tour-1.l
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
(require 'plot)
|
||||
(define *knight-moves*
|
||||
'((2 . 1)(2 . -1 ) (1 . -2) (-1 . -2 )(-2 . -1) (-2 . 1) (-1 . 2) (1 . 2)))
|
||||
(define *hit-squares* null)
|
||||
(define *legal-moves* null)
|
||||
(define *tries* 0)
|
||||
|
||||
(define (square x y n ) (+ y (* x n)))
|
||||
(define (dim n) (1- (* n n))) ; n^2 - 1
|
||||
|
||||
;; check legal knight move from sq
|
||||
;; return null or (list destination-square)
|
||||
|
||||
(define (legal-disp n sq k-move)
|
||||
(let ((x (+ (quotient sq n) (first k-move)))
|
||||
(y (+ (modulo sq n) (rest k-move))))
|
||||
(if (and (>= x 0) (< x n) (>= y 0) (< y n))
|
||||
(list (square x y n)) null)))
|
||||
|
||||
;; list of legal destination squares from sq
|
||||
(define (legal-moves sq k-moves n )
|
||||
(if (null? k-moves) null
|
||||
(append (legal-moves sq (rest k-moves) n) (legal-disp n sq (first k-moves)))))
|
||||
|
||||
;; square freedom = number of destination squares not already reached
|
||||
(define (freedom sq)
|
||||
(for/sum ((dest (vector-ref *legal-moves* sq)))
|
||||
(if (vector-ref *hit-squares* dest) 0 1)))
|
||||
|
||||
;; The chess adage" A knight on the rim is dim" is false here :
|
||||
;; choose to move to square with smallest freedom : Warnsdorf's rule
|
||||
(define (square-sort a b)
|
||||
(< (freedom a) (freedom b)))
|
||||
|
||||
;; knight tour engine
|
||||
(define (play sq step starter last-one wants-open)
|
||||
(set! *tries* (1+ *tries*))
|
||||
(vector-set! *hit-squares* sq step) ;; flag used square
|
||||
(if (= step last-one) (throw 'HIT last-one)) ;; stop on first path found
|
||||
|
||||
(when (or wants-open ;; cut search iff closed path
|
||||
(and (< step last-one) (> (freedom starter) 0))) ;; this ensures a closed path
|
||||
|
||||
(for ((target (list-sort square-sort (vector-ref *legal-moves* sq))))
|
||||
(unless (vector-ref *hit-squares* target)
|
||||
(play target (1+ step) starter last-one wants-open))))
|
||||
(vector-set! *hit-squares* sq #f)) ;; unflag used square
|
||||
|
||||
(define (show-steps n wants-open)
|
||||
(string-delimiter "")
|
||||
(if wants-open
|
||||
(printf "♘-tour: %d tries." *tries*)
|
||||
(printf "♞-closed-tour: %d tries." *tries*))
|
||||
(for ((x n))
|
||||
(writeln)
|
||||
(for((y n))
|
||||
(write (string-pad-right (vector-ref *hit-squares* (square x y n)) 4)))))
|
||||
|
||||
|
||||
(define (k-tour (n 8) (starter 0) (wants-open #t))
|
||||
(set! *hit-squares* (make-vector (* n n) #f))
|
||||
;; build vector of legal moves for squares 0..n^2-1
|
||||
(set! *legal-moves*
|
||||
(build-vector (* n n) (lambda(sq) (legal-moves sq *knight-moves* n))))
|
||||
(set! *tries* 0) ; counter
|
||||
(try
|
||||
(play starter 0 starter (dim n) wants-open)
|
||||
(catch (hit mess) (show-steps n wants-open))))
|
||||
33
Task/Knights-tour/EchoLisp/knights-tour-2.l
Normal file
33
Task/Knights-tour/EchoLisp/knights-tour-2.l
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
(k-tour 8 0 #f)
|
||||
♞-closed-tour: 66 tries.
|
||||
0 47 14 31 62 27 12 29
|
||||
15 32 63 54 13 30 57 26
|
||||
48 1 46 61 56 59 28 11
|
||||
33 16 55 50 53 44 25 58
|
||||
2 49 42 45 60 51 10 39
|
||||
17 34 19 52 43 40 7 24
|
||||
20 3 36 41 22 5 38 9
|
||||
35 18 21 4 37 8 23 6
|
||||
|
||||
(k-tour 20 57)
|
||||
♘-tour: 400 tries.
|
||||
31 34 29 104 209 36 215 300 211 38 213 354 343 40 345 386 383 42 1 388
|
||||
28 103 32 35 216 299 210 37 214 335 342 39 346 385 382 41 390 387 396 43
|
||||
33 30 105 208 201 308 301 336 323 212 353 340 355 344 391 384 395 0 389 2
|
||||
102 27 202 219 298 217 322 309 334 341 356 347 358 351 376 381 378 399 44 397
|
||||
203 106 207 200 307 228 311 302 337 324 339 352 373 364 379 392 375 394 3 368
|
||||
26 101 220 229 218 297 304 321 310 333 348 357 350 359 374 377 380 367 398 45
|
||||
107 204 199 206 227 306 231 312 303 338 325 330 363 372 365 328 393 254 369 4
|
||||
100 25 122 221 230 233 296 305 320 313 332 349 326 329 360 371 366 251 46 253
|
||||
121 108 205 198 145 226 237 232 295 286 319 314 331 362 327 316 255 370 5 178
|
||||
24 99 144 123 222 129 234 279 236 281 294 289 318 315 256 361 250 179 252 47
|
||||
109 120 111 130 197 146 225 238 285 278 287 272 293 290 317 180 257 162 177 6
|
||||
98 23 124 143 128 223 276 235 280 239 282 291 288 265 270 249 176 181 48 161
|
||||
115 110 119 112 131 196 147 224 277 284 273 266 271 292 245 258 163 174 7 58
|
||||
22 97 114 125 142 127 140 275 194 267 240 283 264 269 248 175 182 59 160 49
|
||||
87 116 95 118 113 132 195 148 187 274 263 268 191 244 259 246 173 164 57 8
|
||||
96 21 88 133 126 141 150 139 262 193 190 241 260 247 172 183 60 159 50 65
|
||||
77 86 117 94 89 138 135 188 149 186 261 192 171 184 243 156 165 64 9 56
|
||||
20 81 78 85 134 93 90 151 136 189 170 185 242 155 166 61 158 53 66 51
|
||||
79 76 83 18 91 74 137 16 169 72 153 14 167 70 157 12 63 68 55 10
|
||||
82 19 80 75 84 17 92 73 152 15 168 71 154 13 62 69 54 11 52 67
|
||||
9
Task/Knights-tour/EchoLisp/knights-tour-3.l
Normal file
9
Task/Knights-tour/EchoLisp/knights-tour-3.l
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(define (step-color x y n last-one)
|
||||
(letrec ((sq (square (floor x) (floor y) n))
|
||||
(step (vector-ref *hit-squares* sq) n n))
|
||||
(cond ((= 0 step) (rgb 1 0 0)) ;; red starter
|
||||
((= last-one step) (rgb 0 1 0)) ;; green end
|
||||
(else (gray (// step n n))))))
|
||||
|
||||
(define ( k-plot n)
|
||||
(plot-rgb (lambda (x y) (step-color x y n (dim n))) (- n epsilon) (- n epsilon)))
|
||||
64
Task/Knights-tour/Elixir/knights-tour.elixir
Normal file
64
Task/Knights-tour/Elixir/knights-tour.elixir
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
defmodule Board do
|
||||
import Integer, only: [is_odd: 1]
|
||||
|
||||
defmodule Cell do
|
||||
defstruct [:value, :adj]
|
||||
end
|
||||
|
||||
@adjacent [[-1,-2],[-2,-1],[-2,1],[-1,2],[1,2],[2,1],[2,-1],[1,-2]]
|
||||
|
||||
defp initialize(rows, cols) do
|
||||
board = for i <- 1..rows, j <- 1..cols, into: %{}, do: {{i,j}, true}
|
||||
for i <- 1..rows, j <- 1..cols, into: %{} do
|
||||
adj = for [di,dj] <- @adjacent, board[{i+di, j+dj}], do: {i+di, j+dj}
|
||||
{{i,j}, %Cell{value: 0, adj: adj}}
|
||||
end
|
||||
end
|
||||
|
||||
defp solve(board, ij, num, goal) do
|
||||
board = Map.update!(board, ij, fn cell -> %{cell | value: num} end)
|
||||
if num == goal do
|
||||
throw({:ok, board})
|
||||
else
|
||||
wdof(board, ij)
|
||||
|> Enum.each(fn k -> solve(board, k, num+1, goal) end)
|
||||
end
|
||||
end
|
||||
|
||||
defp wdof(board, ij) do # Warnsdorf's rule
|
||||
board[ij].adj
|
||||
|> Enum.filter(fn k -> board[k].value == 0 end)
|
||||
|> Enum.sort_by(fn k ->
|
||||
Enum.count(board[k].adj, fn x -> board[x].value == 0 end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp to_string(board, rows, cols) do
|
||||
width = to_string(rows * cols) |> String.length
|
||||
format = String.duplicate("~#{width}w ", cols)
|
||||
Enum.map_join(1..rows, "\n", fn i ->
|
||||
:io_lib.fwrite format, (for j <- 1..cols, do: board[{i,j}].value)
|
||||
end)
|
||||
end
|
||||
|
||||
def knight_tour(rows, cols, sx, sy) do
|
||||
IO.puts "\nBoard (#{rows} x #{cols}), Start: [#{sx}, #{sy}]"
|
||||
if is_odd(rows*cols) and is_odd(sx+sy) do
|
||||
IO.puts "No solution"
|
||||
else
|
||||
try do
|
||||
initialize(rows, cols)
|
||||
|> solve({sx,sy}, 1, rows*cols)
|
||||
IO.puts "No solution"
|
||||
catch
|
||||
{:ok, board} -> IO.puts to_string(board, rows, cols)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Board.knight_tour(8,8,4,2)
|
||||
Board.knight_tour(5,5,3,3)
|
||||
Board.knight_tour(4,9,1,1)
|
||||
Board.knight_tour(5,5,1,2)
|
||||
Board.knight_tour(12,12,2,2)
|
||||
345
Task/Knights-tour/Elm/knights-tour.elm
Normal file
345
Task/Knights-tour/Elm/knights-tour.elm
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
module Main exposing (main)
|
||||
|
||||
import Browser exposing (element)
|
||||
import Html as H
|
||||
import Html.Attributes as HA
|
||||
import List exposing (filter, head, length, map, map2, member, tail)
|
||||
import List.Extra exposing (andThen, minimumBy)
|
||||
import String exposing (join)
|
||||
import Svg exposing (g, line, rect, svg)
|
||||
import Svg.Attributes exposing (fill, height, style, version, viewBox, width, x, x1, x2, y, y1, y2)
|
||||
import Svg.Events exposing (onClick)
|
||||
import Time exposing (every)
|
||||
import Tuple
|
||||
|
||||
|
||||
type alias Cell =
|
||||
( Int, Int )
|
||||
|
||||
type alias BoardSize =
|
||||
( Int, Int )
|
||||
|
||||
type alias Model =
|
||||
{ path : List Cell
|
||||
, board : List Cell
|
||||
, pause_ms : Float
|
||||
, size : BoardSize
|
||||
}
|
||||
|
||||
type Msg
|
||||
= Tick Time.Posix
|
||||
| SetStart Cell
|
||||
| SetSize BoardSize
|
||||
| SetPause Float
|
||||
|
||||
boardsize_width: BoardSize -> Int
|
||||
boardsize_width bs =
|
||||
Tuple.second bs
|
||||
|
||||
boardsize_height: BoardSize -> Int
|
||||
boardsize_height bs =
|
||||
Tuple.first bs
|
||||
|
||||
boardsize_dec: Int -> Int
|
||||
boardsize_dec n =
|
||||
let
|
||||
minimum_size = 3
|
||||
in
|
||||
if n <= minimum_size then
|
||||
minimum_size
|
||||
else
|
||||
n - 1
|
||||
boardsize_inc: Int -> Int
|
||||
boardsize_inc n =
|
||||
let
|
||||
maximum_size = 40
|
||||
in
|
||||
if n >= maximum_size then
|
||||
maximum_size
|
||||
else
|
||||
n + 1
|
||||
|
||||
pause_inc: Float -> Float
|
||||
pause_inc n =
|
||||
n + 10
|
||||
|
||||
-- decreasing pause time (ms) increases speed
|
||||
pause_dec: Float -> Float
|
||||
pause_dec n =
|
||||
let
|
||||
minimum_pause = 0
|
||||
in
|
||||
if n <= minimum_pause then
|
||||
minimum_pause
|
||||
else
|
||||
n - 10
|
||||
|
||||
board_init : BoardSize -> List Cell
|
||||
board_init board_size =
|
||||
List.range 0 (boardsize_height board_size - 1)
|
||||
|> andThen
|
||||
(\r ->
|
||||
List.range 0 (boardsize_width board_size - 1)
|
||||
|> andThen
|
||||
(\c ->
|
||||
[ ( r, c ) ]
|
||||
)
|
||||
)
|
||||
|
||||
nextMoves : Model -> Cell -> List Cell
|
||||
nextMoves model ( stRow, stCol ) =
|
||||
let
|
||||
c =
|
||||
[ 1, 2, -1, -2 ]
|
||||
|
||||
km =
|
||||
c
|
||||
|> andThen
|
||||
(\cRow ->
|
||||
c
|
||||
|> andThen
|
||||
(\cCol ->
|
||||
if abs cRow == abs cCol then
|
||||
[]
|
||||
|
||||
else
|
||||
[ ( cRow, cCol ) ]
|
||||
)
|
||||
)
|
||||
|
||||
jumps =
|
||||
List.map (\( kmRow, kmCol ) -> ( kmRow + stRow, kmCol + stCol )) km
|
||||
in
|
||||
List.filter (\j -> List.member j model.board && not (List.member j model.path)) jumps
|
||||
|
||||
|
||||
bestMove : Model -> Maybe Cell
|
||||
bestMove model =
|
||||
case List.head model.path of
|
||||
Just mph ->
|
||||
minimumBy (List.length << nextMoves model) (nextMoves model mph)
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
|
||||
-- Initialize the application - https://guide.elm-lang.org/effects/
|
||||
init : () -> ( Model, Cmd Msg )
|
||||
init _ =
|
||||
let
|
||||
-- Initial board height and width
|
||||
initial_size =
|
||||
8
|
||||
|
||||
-- Initial chess board
|
||||
initial_board =
|
||||
board_init (initial_size, initial_size)
|
||||
|
||||
initial_path =
|
||||
[]
|
||||
initial_pause =
|
||||
10
|
||||
in
|
||||
( Model initial_path initial_board initial_pause (initial_size, initial_size), Cmd.none )
|
||||
|
||||
|
||||
-- View the model - https://guide.elm-lang.org/effects/
|
||||
view : Model -> H.Html Msg
|
||||
view model =
|
||||
let
|
||||
showChecker row col =
|
||||
rect
|
||||
[ x <| String.fromInt col
|
||||
, y <| String.fromInt row
|
||||
, width "1"
|
||||
, height "1"
|
||||
, fill <|
|
||||
if modBy 2 (row + col) == 0 then
|
||||
"blue"
|
||||
|
||||
else
|
||||
"grey"
|
||||
, onClick <| SetStart ( row, col )
|
||||
]
|
||||
[]
|
||||
|
||||
showMove ( row0, col0 ) ( row1, col1 ) =
|
||||
line
|
||||
[ x1 <| String.fromFloat (toFloat col0 + 0.5)
|
||||
, y1 <| String.fromFloat (toFloat row0 + 0.5)
|
||||
, x2 <| String.fromFloat (toFloat col1 + 0.5)
|
||||
, y2 <| String.fromFloat (toFloat row1 + 0.5)
|
||||
, style "stroke:yellow;stroke-width:0.05"
|
||||
]
|
||||
[]
|
||||
|
||||
render mdl =
|
||||
let
|
||||
checkers =
|
||||
mdl.board
|
||||
|> andThen
|
||||
(\( r, c ) ->
|
||||
[ showChecker r c ]
|
||||
)
|
||||
|
||||
moves =
|
||||
case List.tail mdl.path of
|
||||
Nothing ->
|
||||
[]
|
||||
|
||||
Just tl ->
|
||||
List.map2 showMove mdl.path tl
|
||||
in
|
||||
checkers ++ moves
|
||||
|
||||
unvisited =
|
||||
length model.board - length model.path
|
||||
|
||||
center =
|
||||
[ HA.style "text-align" "center" ]
|
||||
|
||||
table =
|
||||
[ HA.style "text-align" "center", HA.style "display" "table", HA.style "width" "auto", HA.style "margin" "auto" ]
|
||||
table_row =
|
||||
[ HA.style "display" "table-row", HA.style "width" "auto" ]
|
||||
|
||||
table_cell =
|
||||
[ HA.style "display" "table-cell", HA.style "width" "auto", HA.style "padding" "1px 3px" ]
|
||||
rows =
|
||||
boardsize_height model.size
|
||||
|
||||
cols =
|
||||
boardsize_width model.size
|
||||
in
|
||||
H.div
|
||||
[]
|
||||
[ H.h1 center [ H.text "Knight's Tour" ]
|
||||
-- controls
|
||||
, H.div
|
||||
table
|
||||
[ H.div -- labels
|
||||
table_row
|
||||
[ H.div
|
||||
table_cell
|
||||
[ H.text "Rows"]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text "Columns"]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text ""]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text "Pause (ms)"]
|
||||
]
|
||||
, H.div
|
||||
table_row
|
||||
[ H.div -- Increase
|
||||
table_cell
|
||||
[ H.button [onClick <| SetSize ( boardsize_inc rows, cols )] [ H.text "▲"] ]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.button [onClick <| SetSize ( rows, boardsize_inc cols )] [ H.text "▲"] ]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text ""]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.button [onClick <| SetPause ( pause_inc model.pause_ms )] [ H.text "▲"] ]
|
||||
]
|
||||
, H.div
|
||||
table_row
|
||||
[ H.div -- Value
|
||||
table_cell
|
||||
[ H.text <| String.fromInt rows ]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text <| String.fromInt cols]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text ""]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text <| String.fromFloat model.pause_ms]
|
||||
]
|
||||
, H.div
|
||||
table_row
|
||||
[ H.div -- Decrease
|
||||
table_cell
|
||||
[ H.button [onClick <| SetSize ( boardsize_dec rows, cols )] [ H.text "▼"] ]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.button [onClick <| SetSize ( rows, boardsize_dec cols )] [ H.text "▼"] ]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.text ""]
|
||||
, H.div
|
||||
table_cell
|
||||
[ H.button [onClick <| SetPause ( pause_dec model.pause_ms )] [ H.text "▼"] ]
|
||||
]
|
||||
]
|
||||
, H.h2 center [ H.text "(pick a square)" ]
|
||||
, H.div -- chess board
|
||||
center
|
||||
[ svg
|
||||
[ version "1.1"
|
||||
, width (String.fromInt (25 * cols))
|
||||
, height (String.fromInt (25 * rows))
|
||||
, viewBox
|
||||
(join " "
|
||||
[ String.fromInt 0
|
||||
, String.fromInt 0
|
||||
, String.fromInt cols
|
||||
, String.fromInt rows
|
||||
]
|
||||
)
|
||||
]
|
||||
[ g [] <| render model ]
|
||||
]
|
||||
, H.h3 center [ H.text <| "Unvisited count : " ++ String.fromInt unvisited ]
|
||||
]
|
||||
|
||||
-- Update the model - https://guide.elm-lang.org/effects/
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
let
|
||||
mo =
|
||||
case msg of
|
||||
SetPause pause ->
|
||||
{ model | pause_ms = pause }
|
||||
|
||||
SetSize board_size ->
|
||||
{ model | board = board_init board_size, path = [], size = board_size }
|
||||
|
||||
SetStart start ->
|
||||
{ model | path = [ start ] }
|
||||
|
||||
Tick _ ->
|
||||
case model.path of
|
||||
[] ->
|
||||
model
|
||||
|
||||
_ ->
|
||||
case bestMove model of
|
||||
Nothing ->
|
||||
model
|
||||
|
||||
Just best ->
|
||||
{ model | path = best :: model.path }
|
||||
in
|
||||
( mo, Cmd.none )
|
||||
|
||||
|
||||
-- Subscribe to https://guide.elm-lang.org/effects/
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Time.every model.pause_ms Tick
|
||||
|
||||
-- Application entry point
|
||||
main: Program () Model Msg
|
||||
main =
|
||||
element -- https://package.elm-lang.org/packages/elm/browser/latest/Browser#element
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
78
Task/Knights-tour/Erlang/knights-tour.erl
Normal file
78
Task/Knights-tour/Erlang/knights-tour.erl
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
-module( knights_tour ).
|
||||
|
||||
-export( [display/1, solve/1, task/0] ).
|
||||
|
||||
display( Moves ) ->
|
||||
%% The knigh walks the moves {Position, Step_nr} order.
|
||||
%% Top left corner is {$a, 8}, Bottom right is {$h, 1}.
|
||||
io:fwrite( "Moves:" ),
|
||||
lists:foldl( fun display_moves/2, erlang:length(Moves), lists:keysort(2, Moves) ),
|
||||
io:nl(),
|
||||
[display_row(Y, Moves) || Y <- lists:seq(8, 1, -1)].
|
||||
|
||||
solve( First_square ) ->
|
||||
try
|
||||
bt_loop( 1, next_moves(First_square), [{First_square, 1}] )
|
||||
|
||||
catch
|
||||
_:{ok, Moves} -> Moves
|
||||
|
||||
end.
|
||||
|
||||
task() ->
|
||||
io:fwrite( "Starting {a, 1}~n" ),
|
||||
Moves = solve( {$a, 1} ),
|
||||
display( Moves ).
|
||||
|
||||
|
||||
|
||||
bt( N, Move, Moves ) -> bt_reject( is_not_allowed_knight_move(Move, Moves), N, Move, [{Move, N} | Moves] ).
|
||||
|
||||
bt_accept( true, _N, _Move, Moves ) -> erlang:throw( {ok, Moves} );
|
||||
bt_accept( false, N, Move, Moves ) -> bt_loop( N, next_moves(Move), Moves ).
|
||||
|
||||
bt_loop( N, New_moves, Moves ) -> [bt( N+1, X, Moves ) || X <- New_moves].
|
||||
|
||||
bt_reject( true, _N, _Move, _Moves ) -> backtrack;
|
||||
bt_reject( false, N, Move, Moves ) -> bt_accept( is_all_knights(Moves), N, Move, Moves ).
|
||||
|
||||
display_moves( {{X, Y}, 1}, Max ) ->
|
||||
io:fwrite(" ~p. N~c~p", [1, X, Y]),
|
||||
Max;
|
||||
display_moves( {{X, Y}, Max}, Max ) ->
|
||||
io:fwrite(" N~c~p~n", [X, Y]),
|
||||
Max;
|
||||
display_moves( {{X, Y}, Step_nr}, Max ) when Step_nr rem 8 =:= 0 ->
|
||||
io:fwrite(" N~c~p~n~p. N~c~p", [X, Y, Step_nr, X, Y]),
|
||||
Max;
|
||||
display_moves( {{X, Y}, Step_nr}, Max ) ->
|
||||
io:fwrite(" N~c~p ~p. N~c~p", [X, Y, Step_nr, X, Y]),
|
||||
Max.
|
||||
|
||||
display_row( Row, Moves ) ->
|
||||
[io:fwrite(" ~2b", [proplists:get_value({X, Row}, Moves)]) || X <- [$a, $b, $c, $d, $e, $f, $g, $h]],
|
||||
io:nl().
|
||||
|
||||
is_all_knights( Moves ) when erlang:length(Moves) =:= 64 -> true;
|
||||
is_all_knights( _Moves ) -> false.
|
||||
|
||||
is_asymetric( Start_column, Start_row, Stop_column, Stop_row ) ->
|
||||
erlang:abs( Start_column - Stop_column ) =/= erlang:abs( Start_row - Stop_row ).
|
||||
|
||||
is_not_allowed_knight_move( Move, Moves ) ->
|
||||
no_such_move =/= proplists:get_value( Move, Moves, no_such_move ).
|
||||
|
||||
next_moves( {Column, Row} ) ->
|
||||
[{X, Y} || X <- next_moves_column(Column), Y <- next_moves_row(Row), is_asymetric(Column, Row, X, Y)].
|
||||
|
||||
next_moves_column( $a ) -> [$b, $c];
|
||||
next_moves_column( $b ) -> [$a, $c, $d];
|
||||
next_moves_column( $g ) -> [$e, $f, $h];
|
||||
next_moves_column( $h ) -> [$g, $f];
|
||||
next_moves_column( C ) -> [C - 2, C - 1, C + 1, C + 2].
|
||||
|
||||
next_moves_row( 1 ) -> [2, 3];
|
||||
next_moves_row( 2 ) -> [1, 3, 4];
|
||||
next_moves_row( 7 ) -> [5, 6, 8];
|
||||
next_moves_row( 8 ) -> [6, 7];
|
||||
next_moves_row( N ) -> [N - 2, N - 1, N + 1, N + 2].
|
||||
651
Task/Knights-tour/Fortran/knights-tour-1.f
Normal file
651
Task/Knights-tour/Fortran/knights-tour-1.f
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
C-----------------------------------------------------------------------
|
||||
C
|
||||
C Find Knight’s Tours.
|
||||
C
|
||||
C Using Warnsdorff’s heuristic, find multiple solutions.
|
||||
C Optionally accept only closed tours.
|
||||
C
|
||||
C This program is migrated from my implementation for ATS/Postiats.
|
||||
C Arrays with dimension 1:64 take the place of stack frames.
|
||||
C
|
||||
C Compile with, for instance:
|
||||
C
|
||||
C gfortran -O2 -g -std=legacy -o knights_tour knights_tour.f
|
||||
C
|
||||
C or
|
||||
C
|
||||
C f2c knights_tour.f
|
||||
C cc -O -o knights_tour knights_tour.c -lf2c
|
||||
C
|
||||
C Usage examples:
|
||||
C
|
||||
C One tour starting at a1, either open or closed:
|
||||
C
|
||||
C echo "a1 1 F" | ./knights_tour
|
||||
C
|
||||
C No more than 2000 closed tours starting at c5:
|
||||
C
|
||||
C echo "c5 2000 T" | ./knights_tour
|
||||
C
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
program ktour
|
||||
implicit none
|
||||
|
||||
character*2 alg
|
||||
integer i, j
|
||||
integer mxtour
|
||||
logical closed
|
||||
|
||||
read (*,*) alg, mxtour, closed
|
||||
call alg2ij (alg, i, j)
|
||||
call explor (i, j, mxtour, closed)
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine explor (istart, jstart, mxtour, closed)
|
||||
implicit none
|
||||
|
||||
C Explore the space of 'Warnsdorffian' knight’s paths, looking for
|
||||
C and printing complete tours.
|
||||
|
||||
integer istart, jstart ! The starting position.
|
||||
integer mxtour ! The maximum number of tours to print.
|
||||
logical closed ! Closed tours only?
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
integer itours
|
||||
logical goodmv
|
||||
logical isclos
|
||||
|
||||
itours = 0
|
||||
call initbd (board)
|
||||
n = 1
|
||||
nmove(1) = 8
|
||||
imove(8, 1) = istart
|
||||
jmove(8, 1) = jstart
|
||||
|
||||
1000 if (itours .lt. mxtour .and. n .ne. 0) then
|
||||
|
||||
if (nmove(n) .eq. 9) then
|
||||
n = n - 1
|
||||
if (n .ne. 0) then
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
nmove(n) = nmove(n) + 1
|
||||
end if
|
||||
else if (goodmv (imove, nmove, n)) then
|
||||
call mkmove (board, imove, jmove, nmove, n)
|
||||
if (n .eq. 64) then
|
||||
if (.not. closed) then
|
||||
itours = itours + 1
|
||||
call prnt (board, itours)
|
||||
else if (isclos (board)) then
|
||||
itours = itours + 1
|
||||
call prnt (board, itours)
|
||||
end if
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
nmove(n) = 9
|
||||
else if (n .eq. 63) then
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
n = n + 1
|
||||
nmove(n) = 1
|
||||
else
|
||||
call nxtmov (board, n, imove, jmove, nmove)
|
||||
n = n + 1
|
||||
nmove(n) = 1
|
||||
end if
|
||||
else
|
||||
nmove(n) = nmove(n) + 1
|
||||
end if
|
||||
|
||||
goto 1000
|
||||
end if
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine initbd (board)
|
||||
implicit none
|
||||
|
||||
C Initialize a chessboard with empty squares.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
do 1010 j = 1, 8
|
||||
do 1000 i = 1, 8
|
||||
board(i, j) = -1
|
||||
1000 continue
|
||||
1010 continue
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine mkmove (board, imove, jmove, nmove, n)
|
||||
implicit none
|
||||
|
||||
C Fill a square with a move number.
|
||||
|
||||
integer board(1:8, 1:8)
|
||||
integer imove(1:8, 1:64)
|
||||
integer jmove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
board(imove(nmove(n), n), jmove(nmove(n), n)) = n
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine unmove (board, imove, jmove, nmove, n)
|
||||
implicit none
|
||||
|
||||
C Unmake a mkmove.
|
||||
|
||||
integer board(1:8, 1:8)
|
||||
integer imove(1:8, 1:64)
|
||||
integer jmove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
board(imove(nmove(n), n), jmove(nmove(n), n)) = -1
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
function goodmv (imove, nmove, n)
|
||||
implicit none
|
||||
|
||||
logical goodmv
|
||||
integer imove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
goodmv = (imove(nmove(n), n) .ne. -1)
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine prnt (board, itours)
|
||||
implicit none
|
||||
|
||||
C Print a knight's tour.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer itours
|
||||
|
||||
10000 format (1X)
|
||||
|
||||
C The following plethora of format statements seemed a simple way to
|
||||
C get this working with f2c. (For gfortran, the 'I0' format
|
||||
C sufficed.)
|
||||
10010 format (1X, "Tour number ", I1)
|
||||
10020 format (1X, "Tour number ", I2)
|
||||
10030 format (1X, "Tour number ", I3)
|
||||
10040 format (1X, "Tour number ", I4)
|
||||
10050 format (1X, "Tour number ", I5)
|
||||
10060 format (1X, "Tour number ", I6)
|
||||
10070 format (1X, "Tour number ", I20)
|
||||
|
||||
if (itours .lt. 10) then
|
||||
write (*, 10010) itours
|
||||
else if (itours .lt. 100) then
|
||||
write (*, 10020) itours
|
||||
else if (itours .lt. 1000) then
|
||||
write (*, 10030) itours
|
||||
else if (itours .lt. 10000) then
|
||||
write (*, 10040) itours
|
||||
else if (itours .lt. 100000) then
|
||||
write (*, 10050) itours
|
||||
else if (itours .lt. 1000000) then
|
||||
write (*, 10060) itours
|
||||
else
|
||||
write (*, 10070) itours
|
||||
end if
|
||||
call prntmv (board)
|
||||
call prntbd (board)
|
||||
write (*, 10000)
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine prntbd (board)
|
||||
implicit none
|
||||
|
||||
C Print a chessboard with the move number in each square.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
10000 format (1X, " ", 8("+----"), "+")
|
||||
10010 format (1X, I2, " ", 8(" | ", I2), " | ")
|
||||
10020 format (1X, " ", 8(" ", A1))
|
||||
|
||||
do 1000 i = 8, 1, -1
|
||||
write (*, 10000)
|
||||
write (*, 10010) i, (board(i, j), j = 1, 8)
|
||||
1000 continue
|
||||
write (*, 10000)
|
||||
write (*, 10020) 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine prntmv (board)
|
||||
implicit none
|
||||
|
||||
C Print the moves of a knight's path, in algebraic notation.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer ipos(1:64)
|
||||
integer jpos(1:64)
|
||||
integer numpos
|
||||
character*2 alg(1:64)
|
||||
integer columns(1:8)
|
||||
integer k
|
||||
integer m
|
||||
|
||||
character*72 lines(1:8)
|
||||
|
||||
10000 format (1X, A)
|
||||
|
||||
call bd2pos (board, ipos, jpos, numpos)
|
||||
|
||||
C Convert the positions to algebraic notation.
|
||||
do 1000 k = 1, numpos
|
||||
call ij2alg (ipos(k), jpos(k), alg(k))
|
||||
1000 continue
|
||||
|
||||
C Fill lines with algebraic notations.
|
||||
do 1020 m = 1, 8
|
||||
columns(m) = 1
|
||||
1020 continue
|
||||
m = 1
|
||||
do 1100 k = 1, numpos
|
||||
lines(m)(columns(m) : columns(m) + 1) = alg(k)(1:2)
|
||||
columns(m) = columns(m) + 2
|
||||
if (k .ne. numpos) then
|
||||
lines(m)(columns(m) : columns(m) + 3) = " -> "
|
||||
columns(m) = columns(m) + 4
|
||||
else if (numpos .eq. 64 .and.
|
||||
$ ((abs (ipos(numpos) - ipos(1)) .eq. 2
|
||||
$ .and. abs (jpos(numpos) - jpos(1)) .eq. 1) .or.
|
||||
$ ((abs (ipos(numpos) - ipos(1)) .eq. 1
|
||||
$ .and. abs (jpos(numpos) - jpos(1)) .eq. 2)))) then
|
||||
lines(m)(columns(m) : columns(m) + 8) = " -> cycle"
|
||||
columns(m) = columns(m) + 9
|
||||
endif
|
||||
if (mod (k, 8) .eq. 0) m = m + 1
|
||||
1100 continue
|
||||
|
||||
C Print the lines that have stuff in them.
|
||||
do 1200 m = 1, 8
|
||||
if (columns(m) .ne. 1) then
|
||||
write (*, 10000) lines(m)(1 : columns(m) - 1)
|
||||
end if
|
||||
1200 continue
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
function isclos (board)
|
||||
implicit none
|
||||
|
||||
C Is a board a closed tour?
|
||||
|
||||
logical isclos
|
||||
integer board(1:8,1:8)
|
||||
integer ipos(1:64) ! The i-positions in order.
|
||||
integer jpos(1:64) ! The j-positions in order.
|
||||
integer numpos ! The number of positions so far.
|
||||
|
||||
call bd2pos (board, ipos, jpos, numpos)
|
||||
|
||||
isclos = (numpos .eq. 64 .and.
|
||||
$ ((abs (ipos(numpos) - ipos(1)) .eq. 2
|
||||
$ .and. abs (jpos(numpos) - jpos(1)) .eq. 1) .or.
|
||||
$ ((abs (ipos(numpos) - ipos(1)) .eq. 1
|
||||
$ .and. abs (jpos(numpos) - jpos(1)) .eq. 2))))
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine bd2pos (board, ipos, jpos, numpos)
|
||||
implicit none
|
||||
|
||||
C Convert from a board to a list of board positions.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer ipos(1:64) ! The i-positions in order.
|
||||
integer jpos(1:64) ! The j-positions in order.
|
||||
integer numpos ! The number of positions so far.
|
||||
|
||||
integer i, j
|
||||
|
||||
numpos = 0
|
||||
do 1010 i = 1, 8
|
||||
do 1000 j = 1, 8
|
||||
if (board(i, j) .ne. -1) then
|
||||
numpos = max (board(i, j), numpos)
|
||||
ipos(board(i, j)) = i
|
||||
jpos(board(i, j)) = j
|
||||
end if
|
||||
1000 continue
|
||||
1010 continue
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine nxtmov (board, n, imove, jmove, nmove)
|
||||
implicit none
|
||||
|
||||
C Find possible next moves. Prune and sort the moves according to
|
||||
C Warnsdorff's heuristic, keeping only those that have the minimum
|
||||
C number of legal following moves.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
|
||||
integer w1, w2, w3, w4, w5, w6, w7, w8
|
||||
integer w
|
||||
integer n1
|
||||
integer pickw
|
||||
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
|
||||
n1 = n + 1
|
||||
nmove(n1) = 1
|
||||
call countf (board, n1, imove, jmove, nmove, w1)
|
||||
nmove(n1) = 2
|
||||
call countf (board, n1, imove, jmove, nmove, w2)
|
||||
nmove(n1) = 3
|
||||
call countf (board, n1, imove, jmove, nmove, w3)
|
||||
nmove(n1) = 4
|
||||
call countf (board, n1, imove, jmove, nmove, w4)
|
||||
nmove(n1) = 5
|
||||
call countf (board, n1, imove, jmove, nmove, w5)
|
||||
nmove(n1) = 6
|
||||
call countf (board, n1, imove, jmove, nmove, w6)
|
||||
nmove(n1) = 7
|
||||
call countf (board, n1, imove, jmove, nmove, w7)
|
||||
nmove(n1) = 8
|
||||
call countf (board, n1, imove, jmove, nmove, w8)
|
||||
|
||||
w = pickw (w1, w2, w3, w4, w5, w6, w7, w8)
|
||||
|
||||
if (w .eq. 0) then
|
||||
call disabl (imove(1, n1), jmove(1, n1))
|
||||
call disabl (imove(2, n1), jmove(2, n1))
|
||||
call disabl (imove(3, n1), jmove(3, n1))
|
||||
call disabl (imove(4, n1), jmove(4, n1))
|
||||
call disabl (imove(5, n1), jmove(5, n1))
|
||||
call disabl (imove(6, n1), jmove(6, n1))
|
||||
call disabl (imove(7, n1), jmove(7, n1))
|
||||
call disabl (imove(8, n1), jmove(8, n1))
|
||||
else
|
||||
if (w .ne. w1) call disabl (imove(1, n1), jmove(1, n1))
|
||||
if (w .ne. w2) call disabl (imove(2, n1), jmove(2, n1))
|
||||
if (w .ne. w3) call disabl (imove(3, n1), jmove(3, n1))
|
||||
if (w .ne. w4) call disabl (imove(4, n1), jmove(4, n1))
|
||||
if (w .ne. w5) call disabl (imove(5, n1), jmove(5, n1))
|
||||
if (w .ne. w6) call disabl (imove(6, n1), jmove(6, n1))
|
||||
if (w .ne. w7) call disabl (imove(7, n1), jmove(7, n1))
|
||||
if (w .ne. w8) call disabl (imove(8, n1), jmove(8, n1))
|
||||
end if
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine countf (board, n, imove, jmove, nmove, w)
|
||||
implicit none
|
||||
|
||||
C Count the number of moves possible after an nth move.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
integer w
|
||||
|
||||
logical goodmv
|
||||
integer n1
|
||||
|
||||
if (goodmv (imove, nmove, n)) then
|
||||
call mkmove (board, imove, jmove, nmove, n)
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
n1 = n + 1
|
||||
w = 0
|
||||
if (imove(1, n1) .ne. -1) w = w + 1
|
||||
if (imove(2, n1) .ne. -1) w = w + 1
|
||||
if (imove(3, n1) .ne. -1) w = w + 1
|
||||
if (imove(4, n1) .ne. -1) w = w + 1
|
||||
if (imove(5, n1) .ne. -1) w = w + 1
|
||||
if (imove(6, n1) .ne. -1) w = w + 1
|
||||
if (imove(7, n1) .ne. -1) w = w + 1
|
||||
if (imove(8, n1) .ne. -1) w = w + 1
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
else
|
||||
C The nth move itself is impossible.
|
||||
w = 0
|
||||
end if
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
function pickw (w1, w2, w3, w4, w5, w6, w7, w8)
|
||||
implicit none
|
||||
|
||||
C From w1..w8, pick out the least nonzero value (or zero if they all
|
||||
C equal zero).
|
||||
|
||||
integer pickw
|
||||
integer w1, w2, w3, w4, w5, w6, w7, w8
|
||||
|
||||
integer w
|
||||
integer pickw1
|
||||
|
||||
w = 0
|
||||
w = pickw1 (w, w1)
|
||||
w = pickw1 (w, w2)
|
||||
w = pickw1 (w, w3)
|
||||
w = pickw1 (w, w4)
|
||||
w = pickw1 (w, w5)
|
||||
w = pickw1 (w, w6)
|
||||
w = pickw1 (w, w7)
|
||||
w = pickw1 (w, w8)
|
||||
|
||||
pickw = w
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
function pickw1 (u, v)
|
||||
implicit none
|
||||
|
||||
C A small function used by pickw.
|
||||
|
||||
integer pickw1
|
||||
integer u, v
|
||||
|
||||
if (v .eq. 0) then
|
||||
pickw1 = u
|
||||
else if (u .eq. 0) then
|
||||
pickw1 = v
|
||||
else
|
||||
pickw1 = min (u, v)
|
||||
end if
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine possib (board, n, imove, jmove, nmove)
|
||||
implicit none
|
||||
|
||||
C Find moves that are possible from an nth-move position.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
|
||||
integer i, j
|
||||
integer n1
|
||||
|
||||
i = imove(nmove(n), n)
|
||||
j = jmove(nmove(n), n)
|
||||
n1 = n + 1
|
||||
call trymov (board, i + 1, j + 2, imove(1, n1), jmove(1, n1))
|
||||
call trymov (board, i + 2, j + 1, imove(2, n1), jmove(2, n1))
|
||||
call trymov (board, i + 1, j - 2, imove(3, n1), jmove(3, n1))
|
||||
call trymov (board, i + 2, j - 1, imove(4, n1), jmove(4, n1))
|
||||
call trymov (board, i - 1, j + 2, imove(5, n1), jmove(5, n1))
|
||||
call trymov (board, i - 2, j + 1, imove(6, n1), jmove(6, n1))
|
||||
call trymov (board, i - 1, j - 2, imove(7, n1), jmove(7, n1))
|
||||
call trymov (board, i - 2, j - 1, imove(8, n1), jmove(8, n1))
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine trymov (board, i, j, imove, jmove)
|
||||
implicit none
|
||||
|
||||
C Try a move to square (i, j).
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer i, j
|
||||
integer imove, jmove
|
||||
|
||||
call disabl (imove, jmove)
|
||||
if (1 .le. i .and. i .le. 8 .and. 1 .le. j .and. j .le. 8) then
|
||||
if (board(i,j) .eq. -1) then
|
||||
call enable (i, j, imove, jmove)
|
||||
end if
|
||||
end if
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine enable (i, j, imove, jmove)
|
||||
implicit none
|
||||
|
||||
C Enable a potential move.
|
||||
|
||||
integer i, j
|
||||
integer imove, jmove
|
||||
|
||||
imove = i
|
||||
jmove = j
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine disabl (imove, jmove)
|
||||
implicit none
|
||||
|
||||
C Disable a potential move.
|
||||
|
||||
integer imove, jmove
|
||||
|
||||
imove = -1
|
||||
jmove = -1
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine alg2ij (alg, i, j)
|
||||
implicit none
|
||||
|
||||
C Convert, for instance, 'c5' to i=3,j=5.
|
||||
|
||||
character*2 alg
|
||||
integer i, j
|
||||
|
||||
if (alg(1:1) .eq. 'a') j = 1
|
||||
if (alg(1:1) .eq. 'b') j = 2
|
||||
if (alg(1:1) .eq. 'c') j = 3
|
||||
if (alg(1:1) .eq. 'd') j = 4
|
||||
if (alg(1:1) .eq. 'e') j = 5
|
||||
if (alg(1:1) .eq. 'f') j = 6
|
||||
if (alg(1:1) .eq. 'g') j = 7
|
||||
if (alg(1:1) .eq. 'h') j = 8
|
||||
|
||||
if (alg(2:2) .eq. '1') i = 1
|
||||
if (alg(2:2) .eq. '2') i = 2
|
||||
if (alg(2:2) .eq. '3') i = 3
|
||||
if (alg(2:2) .eq. '4') i = 4
|
||||
if (alg(2:2) .eq. '5') i = 5
|
||||
if (alg(2:2) .eq. '6') i = 6
|
||||
if (alg(2:2) .eq. '7') i = 7
|
||||
if (alg(2:2) .eq. '8') i = 8
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
|
||||
subroutine ij2alg (i, j, alg)
|
||||
implicit none
|
||||
|
||||
C Convert, for instance, i=3,j=5 to 'c5'.
|
||||
|
||||
integer i, j
|
||||
character*2 alg
|
||||
|
||||
character alg1
|
||||
character alg2
|
||||
|
||||
if (j .eq. 1) alg1 = 'a'
|
||||
if (j .eq. 2) alg1 = 'b'
|
||||
if (j .eq. 3) alg1 = 'c'
|
||||
if (j .eq. 4) alg1 = 'd'
|
||||
if (j .eq. 5) alg1 = 'e'
|
||||
if (j .eq. 6) alg1 = 'f'
|
||||
if (j .eq. 7) alg1 = 'g'
|
||||
if (j .eq. 8) alg1 = 'h'
|
||||
|
||||
if (i .eq. 1) alg2 = '1'
|
||||
if (i .eq. 2) alg2 = '2'
|
||||
if (i .eq. 3) alg2 = '3'
|
||||
if (i .eq. 4) alg2 = '4'
|
||||
if (i .eq. 5) alg2 = '5'
|
||||
if (i .eq. 6) alg2 = '6'
|
||||
if (i .eq. 7) alg2 = '7'
|
||||
if (i .eq. 8) alg2 = '8'
|
||||
|
||||
alg(1:1) = alg1
|
||||
alg(2:2) = alg2
|
||||
|
||||
end
|
||||
|
||||
C-----------------------------------------------------------------------
|
||||
561
Task/Knights-tour/Fortran/knights-tour-2.f
Normal file
561
Task/Knights-tour/Fortran/knights-tour-2.f
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
!-----------------------------------------------------------------------
|
||||
!
|
||||
! Find Knight’s Tours.
|
||||
!
|
||||
! Using Warnsdorff’s heuristic, find multiple solutions.
|
||||
! Optionally accept only closed tours.
|
||||
!
|
||||
! This program is migrated from my implementation for
|
||||
! ATS/Postiats. Unlike my FORTRAN 77 implementation (which simply
|
||||
! cannot do so), it uses a recursive call.
|
||||
!
|
||||
! Compile with, for instance:
|
||||
!
|
||||
! gfortran -O2 -g -std=f95 -o knights_tour knights_tour.f90
|
||||
!
|
||||
! Usage examples:
|
||||
!
|
||||
! One tour starting at a1, either open or closed:
|
||||
!
|
||||
! echo "a1 1 F" | ./knights_tour
|
||||
!
|
||||
! No more than 2000 closed tours starting at c5:
|
||||
!
|
||||
! echo "c5 2000 T" | ./knights_tour
|
||||
!
|
||||
!-----------------------------------------------------------------------
|
||||
|
||||
program knights_tour
|
||||
implicit none
|
||||
|
||||
character(len = 2) inp__alg
|
||||
integer inp__istart
|
||||
integer inp__jstart
|
||||
integer inp__max_tours
|
||||
logical inp__closed
|
||||
|
||||
read (*,*) inp__alg, inp__max_tours, inp__closed
|
||||
call alg2ij (inp__alg, inp__istart, inp__jstart)
|
||||
call main (inp__istart, inp__jstart, inp__max_tours, inp__closed)
|
||||
|
||||
contains
|
||||
|
||||
subroutine main (istart, jstart, max_tours, closed)
|
||||
integer, intent(in) :: istart, jstart ! The starting position.
|
||||
integer, intent(in) :: max_tours ! The max. no. of tours to print.
|
||||
logical, intent(in) :: closed ! Closed tours only?
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer num_tours_printed
|
||||
|
||||
num_tours_printed = 0
|
||||
call init_board (board)
|
||||
call explore (board, 1, istart, jstart, max_tours, &
|
||||
& num_tours_printed, closed)
|
||||
end subroutine main
|
||||
|
||||
recursive subroutine explore (board, n, i, j, max_tours, &
|
||||
& num_tours_printed, closed)
|
||||
|
||||
! Recursively the space of 'Warnsdorffian' knight’s paths, looking
|
||||
! for and printing complete tours.
|
||||
|
||||
integer, intent(inout) :: board(1:8,1:8)
|
||||
integer, intent(in) :: n
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(in) :: max_tours
|
||||
integer, intent(inout) :: num_tours_printed
|
||||
logical, intent(in) :: closed
|
||||
|
||||
integer imove(1:8)
|
||||
integer jmove(1:8)
|
||||
integer k
|
||||
|
||||
if (num_tours_printed < max_tours .and. n /= 0) then
|
||||
if (is_good_move (i, j)) then
|
||||
call mkmove (board, i, j, n)
|
||||
if (n == 63) then
|
||||
call find_possible_moves (board, i, j, imove, jmove)
|
||||
call try_last_move (board, n + 1, imove(1), jmove(1), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(2), jmove(2), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(3), jmove(3), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(4), jmove(4), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(5), jmove(5), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(6), jmove(6), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(7), jmove(7), &
|
||||
& num_tours_printed, closed)
|
||||
call try_last_move (board, n + 1, imove(8), jmove(8), &
|
||||
& num_tours_printed, closed)
|
||||
else
|
||||
call find_next_moves (board, n, i, j, imove, jmove)
|
||||
do k = 1, 8
|
||||
if (is_good_move (imove(k), jmove(k))) then
|
||||
!
|
||||
! Here is the recursive call.
|
||||
!
|
||||
call explore (board, n + 1, imove(k), jmove(k), &
|
||||
& max_tours, num_tours_printed, closed)
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
call unmove (board, i, j)
|
||||
end if
|
||||
end if
|
||||
end subroutine explore
|
||||
|
||||
subroutine try_last_move (board, n, i, j, num_tours_printed, closed)
|
||||
integer, intent(inout) :: board(1:8,1:8)
|
||||
integer, intent(in) :: n
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(inout) :: num_tours_printed
|
||||
logical, intent(in) :: closed
|
||||
|
||||
integer ipos(1:64)
|
||||
integer jpos(1:64)
|
||||
integer numpos
|
||||
integer idiff
|
||||
integer jdiff
|
||||
|
||||
if (is_good_move (i, j)) then
|
||||
call mkmove (board, i, j, n)
|
||||
if (.not. closed) then
|
||||
num_tours_printed = num_tours_printed + 1
|
||||
call print_tour (board, num_tours_printed)
|
||||
else
|
||||
call board2positions (board, ipos, jpos, numpos)
|
||||
idiff = abs (i - ipos(1))
|
||||
jdiff = abs (j - jpos(1))
|
||||
if ((idiff == 1 .and. jdiff == 2) .or. &
|
||||
(idiff == 2 .and. jdiff == 1)) then
|
||||
num_tours_printed = num_tours_printed + 1
|
||||
call print_tour (board, num_tours_printed)
|
||||
end if
|
||||
end if
|
||||
call unmove (board, i, j)
|
||||
end if
|
||||
end subroutine try_last_move
|
||||
|
||||
subroutine init_board (board)
|
||||
|
||||
! Initialize a chessboard with empty squares.
|
||||
|
||||
integer, intent(out) :: board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
do j = 1, 8
|
||||
do i = 1, 8
|
||||
board(i, j) = -1
|
||||
end do
|
||||
end do
|
||||
end subroutine init_board
|
||||
|
||||
subroutine mkmove (board, i, j, n)
|
||||
|
||||
! Fill a square with a move number.
|
||||
|
||||
integer, intent(inout) :: board(1:8, 1:8)
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(in) :: n
|
||||
|
||||
board(i, j) = n
|
||||
end subroutine mkmove
|
||||
|
||||
subroutine unmove (board, i, j)
|
||||
|
||||
! Unmake a mkmove.
|
||||
|
||||
integer, intent(inout) :: board(1:8, 1:8)
|
||||
integer, intent(in) :: i, j
|
||||
|
||||
board(i, j) = -1
|
||||
end subroutine unmove
|
||||
|
||||
function is_good_move (i, j)
|
||||
logical is_good_move
|
||||
integer, intent(in) :: i, j
|
||||
|
||||
is_good_move = (i /= -1 .and. j /= -1)
|
||||
end function is_good_move
|
||||
|
||||
subroutine print_tour (board, num_tours_printed)
|
||||
|
||||
! Print a knight's tour.
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
integer, intent(in) :: num_tours_printed
|
||||
|
||||
write (*, '("Tour number ", I0)') num_tours_printed
|
||||
call print_moves (board)
|
||||
call print_board (board)
|
||||
write (*, '()')
|
||||
end subroutine print_tour
|
||||
|
||||
subroutine print_board (board)
|
||||
|
||||
! Print a chessboard with the move number in each square.
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
do i = 8, 1, -1
|
||||
write (*, '(" ", 8("+----"), "+")')
|
||||
write (*, '(I2, " ", 8(" | ", I2), " | ")') &
|
||||
i, (board(i, j), j = 1, 8)
|
||||
end do
|
||||
write (*, '(" ", 8("+----"), "+")')
|
||||
write (*, '(" ", 8(" ", A1))') &
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
|
||||
|
||||
end subroutine print_board
|
||||
|
||||
subroutine print_moves (board)
|
||||
|
||||
! Print the moves of a knight's path, in algebraic notation.
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
|
||||
integer ipos(1:64)
|
||||
integer jpos(1:64)
|
||||
integer numpos
|
||||
character(len = 2) alg(1:64)
|
||||
integer columns(1:8)
|
||||
integer k
|
||||
integer m
|
||||
|
||||
character(len = 72) lines(1:8)
|
||||
|
||||
call board2positions (board, ipos, jpos, numpos)
|
||||
|
||||
! Convert the positions to algebraic notation.
|
||||
do k = 1, numpos
|
||||
call ij2alg (ipos(k), jpos(k), alg(k))
|
||||
end do
|
||||
|
||||
! Fill lines with algebraic notations.
|
||||
do m = 1, 8
|
||||
columns(m) = 1
|
||||
end do
|
||||
m = 1
|
||||
do k = 1, numpos
|
||||
lines(m)(columns(m) : columns(m) + 1) = alg(k)(1:2)
|
||||
columns(m) = columns(m) + 2
|
||||
if (k /= numpos) then
|
||||
lines(m)(columns(m) : columns(m) + 3) = " -> "
|
||||
columns(m) = columns(m) + 4
|
||||
else if (numpos == 64 .and. &
|
||||
((abs (ipos(numpos) - ipos(1)) == 2 &
|
||||
.and. abs (jpos(numpos) - jpos(1)) == 1) .or. &
|
||||
((abs (ipos(numpos) - ipos(1)) == 1 &
|
||||
.and. abs (jpos(numpos) - jpos(1)) == 2)))) then
|
||||
lines(m)(columns(m) : columns(m) + 8) = " -> cycle"
|
||||
columns(m) = columns(m) + 9
|
||||
endif
|
||||
if (mod (k, 8) == 0) m = m + 1
|
||||
end do
|
||||
|
||||
! Print the lines that have stuff in them.
|
||||
do m = 1, 8
|
||||
if (columns(m) /= 1) then
|
||||
write (*, '(A)') lines(m)(1 : columns(m) - 1)
|
||||
end if
|
||||
end do
|
||||
|
||||
end subroutine print_moves
|
||||
|
||||
function is_closed (board)
|
||||
|
||||
! Is a board a closed tour?
|
||||
|
||||
logical is_closed
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer ipos(1:64) ! The i-positions in order.
|
||||
integer jpos(1:64) ! The j-positions in order.
|
||||
integer numpos ! The number of positions so far.
|
||||
|
||||
call board2positions (board, ipos, jpos, numpos)
|
||||
|
||||
is_closed = (numpos == 64 .and. &
|
||||
((abs (ipos(numpos) - ipos(1)) == 2 &
|
||||
.and. abs (jpos(numpos) - jpos(1)) == 1) .or. &
|
||||
((abs (ipos(numpos) - ipos(1)) == 1 &
|
||||
.and. abs (jpos(numpos) - jpos(1)) == 2))))
|
||||
|
||||
end function is_closed
|
||||
|
||||
subroutine board2positions (board, ipos, jpos, numpos)
|
||||
|
||||
! Convert from a board to a list of board positions.
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
integer, intent(out) :: ipos(1:64) ! The i-positions in order.
|
||||
integer, intent(out) :: jpos(1:64) ! The j-positions in order.
|
||||
integer, intent(out) :: numpos ! The number of positions so far.
|
||||
|
||||
integer i, j
|
||||
|
||||
numpos = 0
|
||||
do i = 1, 8
|
||||
do j = 1, 8
|
||||
if (board(i, j) /= -1) then
|
||||
numpos = max (board(i, j), numpos)
|
||||
ipos(board(i, j)) = i
|
||||
jpos(board(i, j)) = j
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end subroutine board2positions
|
||||
|
||||
subroutine find_next_moves (board, n, i, j, imove, jmove)
|
||||
|
||||
! Find possible next moves. Prune and sort the moves according to
|
||||
! Warnsdorff's heuristic, keeping only those that have the minimum
|
||||
! number of legal following moves.
|
||||
|
||||
integer, intent(inout) :: board(1:8,1:8)
|
||||
integer, intent(in) :: n
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(inout) :: imove(1:8)
|
||||
integer, intent(inout) :: jmove(1:8)
|
||||
|
||||
integer w1, w2, w3, w4, w5, w6, w7, w8
|
||||
integer w
|
||||
|
||||
call find_possible_moves (board, i, j, imove, jmove)
|
||||
|
||||
call count_following (board, n + 1, imove(1), jmove(1), w1)
|
||||
call count_following (board, n + 1, imove(2), jmove(2), w2)
|
||||
call count_following (board, n + 1, imove(3), jmove(3), w3)
|
||||
call count_following (board, n + 1, imove(4), jmove(4), w4)
|
||||
call count_following (board, n + 1, imove(5), jmove(5), w5)
|
||||
call count_following (board, n + 1, imove(6), jmove(6), w6)
|
||||
call count_following (board, n + 1, imove(7), jmove(7), w7)
|
||||
call count_following (board, n + 1, imove(8), jmove(8), w8)
|
||||
|
||||
w = pick_w (w1, w2, w3, w4, w5, w6, w7, w8)
|
||||
|
||||
if (w == 0) then
|
||||
call disable (imove(1), jmove(1))
|
||||
call disable (imove(2), jmove(2))
|
||||
call disable (imove(3), jmove(3))
|
||||
call disable (imove(4), jmove(4))
|
||||
call disable (imove(5), jmove(5))
|
||||
call disable (imove(6), jmove(6))
|
||||
call disable (imove(7), jmove(7))
|
||||
call disable (imove(8), jmove(8))
|
||||
else
|
||||
if (w /= w1) call disable (imove(1), jmove(1))
|
||||
if (w /= w2) call disable (imove(2), jmove(2))
|
||||
if (w /= w3) call disable (imove(3), jmove(3))
|
||||
if (w /= w4) call disable (imove(4), jmove(4))
|
||||
if (w /= w5) call disable (imove(5), jmove(5))
|
||||
if (w /= w6) call disable (imove(6), jmove(6))
|
||||
if (w /= w7) call disable (imove(7), jmove(7))
|
||||
if (w /= w8) call disable (imove(8), jmove(8))
|
||||
end if
|
||||
|
||||
end subroutine find_next_moves
|
||||
|
||||
subroutine count_following (board, n, i, j, w)
|
||||
|
||||
! Count the number of moves possible after an nth move.
|
||||
|
||||
integer, intent(inout) :: board(1:8,1:8)
|
||||
integer, intent(in) :: n
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(out) :: w
|
||||
|
||||
integer imove(1:8)
|
||||
integer jmove(1:8)
|
||||
|
||||
if (is_good_move (i, j)) then
|
||||
call mkmove (board, i, j, n)
|
||||
call find_possible_moves (board, i, j, imove, jmove)
|
||||
w = 0
|
||||
if (is_good_move (imove(1), jmove(1))) w = w + 1
|
||||
if (is_good_move (imove(2), jmove(2))) w = w + 1
|
||||
if (is_good_move (imove(3), jmove(3))) w = w + 1
|
||||
if (is_good_move (imove(4), jmove(4))) w = w + 1
|
||||
if (is_good_move (imove(5), jmove(5))) w = w + 1
|
||||
if (is_good_move (imove(6), jmove(6))) w = w + 1
|
||||
if (is_good_move (imove(7), jmove(7))) w = w + 1
|
||||
if (is_good_move (imove(8), jmove(8))) w = w + 1
|
||||
call unmove (board, i, j)
|
||||
else
|
||||
! The nth move itself is impossible.
|
||||
w = 0
|
||||
end if
|
||||
|
||||
end subroutine count_following
|
||||
|
||||
function pick_w (w1, w2, w3, w4, w5, w6, w7, w8) result (w)
|
||||
|
||||
! From w1..w8, pick out the least nonzero value (or zero if they
|
||||
! all equal zero).
|
||||
|
||||
integer, intent(in) :: w1, w2, w3, w4, w5, w6, w7, w8
|
||||
integer w
|
||||
|
||||
w = 0
|
||||
w = pick_w1 (w, w1)
|
||||
w = pick_w1 (w, w2)
|
||||
w = pick_w1 (w, w3)
|
||||
w = pick_w1 (w, w4)
|
||||
w = pick_w1 (w, w5)
|
||||
w = pick_w1 (w, w6)
|
||||
w = pick_w1 (w, w7)
|
||||
w = pick_w1 (w, w8)
|
||||
end function pick_w
|
||||
|
||||
function pick_w1 (u, v)
|
||||
|
||||
! A small function used by pick_w.
|
||||
|
||||
integer pick_w1
|
||||
integer, intent(in) :: u, v
|
||||
|
||||
if (v == 0) then
|
||||
pick_w1 = u
|
||||
else if (u == 0) then
|
||||
pick_w1 = v
|
||||
else
|
||||
pick_w1 = min (u, v)
|
||||
end if
|
||||
end function pick_w1
|
||||
|
||||
subroutine find_possible_moves (board, i, j, imove, jmove)
|
||||
|
||||
! Find moves that are possible from a position.
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(out) :: imove(1:8)
|
||||
integer, intent(out) :: jmove(1:8)
|
||||
|
||||
call trymov (board, i + 1, j + 2, imove(1), jmove(1))
|
||||
call trymov (board, i + 2, j + 1, imove(2), jmove(2))
|
||||
call trymov (board, i + 1, j - 2, imove(3), jmove(3))
|
||||
call trymov (board, i + 2, j - 1, imove(4), jmove(4))
|
||||
call trymov (board, i - 1, j + 2, imove(5), jmove(5))
|
||||
call trymov (board, i - 2, j + 1, imove(6), jmove(6))
|
||||
call trymov (board, i - 1, j - 2, imove(7), jmove(7))
|
||||
call trymov (board, i - 2, j - 1, imove(8), jmove(8))
|
||||
end subroutine find_possible_moves
|
||||
|
||||
subroutine trymov (board, i, j, imove, jmove)
|
||||
|
||||
! Try a move to square (i, j).
|
||||
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(inout) :: imove, jmove
|
||||
|
||||
call disable (imove, jmove)
|
||||
if (1 <= i .and. i <= 8 .and. 1 <= j .and. j <= 8) then
|
||||
if (square_is_empty (board, i, j)) then
|
||||
call enable (i, j, imove, jmove)
|
||||
end if
|
||||
end if
|
||||
|
||||
end subroutine trymov
|
||||
|
||||
function square_is_empty (board, i, j)
|
||||
logical square_is_empty
|
||||
integer, intent(in) :: board(1:8,1:8)
|
||||
integer, intent(in) :: i, j
|
||||
|
||||
square_is_empty = (board(i, j) == -1)
|
||||
end function square_is_empty
|
||||
|
||||
subroutine enable (i, j, imove, jmove)
|
||||
|
||||
! Enable a potential move.
|
||||
|
||||
integer, intent(in) :: i, j
|
||||
integer, intent(inout) :: imove, jmove
|
||||
|
||||
imove = i
|
||||
jmove = j
|
||||
end subroutine enable
|
||||
|
||||
subroutine disable (imove, jmove)
|
||||
|
||||
! Disable a potential move.
|
||||
|
||||
integer, intent(out) :: imove, jmove
|
||||
|
||||
imove = -1
|
||||
jmove = -1
|
||||
end subroutine disable
|
||||
|
||||
subroutine alg2ij (alg, i, j)
|
||||
|
||||
! Convert, for instance, 'c5' to i=3,j=5.
|
||||
|
||||
character(len = 2), intent(in) :: alg
|
||||
integer, intent(out) :: i, j
|
||||
|
||||
if (alg(1:1) == 'a') j = 1
|
||||
if (alg(1:1) == 'b') j = 2
|
||||
if (alg(1:1) == 'c') j = 3
|
||||
if (alg(1:1) == 'd') j = 4
|
||||
if (alg(1:1) == 'e') j = 5
|
||||
if (alg(1:1) == 'f') j = 6
|
||||
if (alg(1:1) == 'g') j = 7
|
||||
if (alg(1:1) == 'h') j = 8
|
||||
|
||||
if (alg(2:2) == '1') i = 1
|
||||
if (alg(2:2) == '2') i = 2
|
||||
if (alg(2:2) == '3') i = 3
|
||||
if (alg(2:2) == '4') i = 4
|
||||
if (alg(2:2) == '5') i = 5
|
||||
if (alg(2:2) == '6') i = 6
|
||||
if (alg(2:2) == '7') i = 7
|
||||
if (alg(2:2) == '8') i = 8
|
||||
|
||||
end subroutine alg2ij
|
||||
|
||||
subroutine ij2alg (i, j, alg)
|
||||
|
||||
! Convert, for instance, i=3,j=5 to 'c5'.
|
||||
|
||||
integer, intent(in) :: i, j
|
||||
character(len = 2), intent(out) :: alg
|
||||
|
||||
character alg1
|
||||
character alg2
|
||||
|
||||
if (j == 1) alg1 = 'a'
|
||||
if (j == 2) alg1 = 'b'
|
||||
if (j == 3) alg1 = 'c'
|
||||
if (j == 4) alg1 = 'd'
|
||||
if (j == 5) alg1 = 'e'
|
||||
if (j == 6) alg1 = 'f'
|
||||
if (j == 7) alg1 = 'g'
|
||||
if (j == 8) alg1 = 'h'
|
||||
|
||||
if (i == 1) alg2 = '1'
|
||||
if (i == 2) alg2 = '2'
|
||||
if (i == 3) alg2 = '3'
|
||||
if (i == 4) alg2 = '4'
|
||||
if (i == 5) alg2 = '5'
|
||||
if (i == 6) alg2 = '6'
|
||||
if (i == 7) alg2 = '7'
|
||||
if (i == 8) alg2 = '8'
|
||||
|
||||
alg(1:1) = alg1
|
||||
alg(2:2) = alg2
|
||||
|
||||
end subroutine ij2alg
|
||||
|
||||
end program
|
||||
|
||||
!-----------------------------------------------------------------------
|
||||
326
Task/Knights-tour/Fortran/knights-tour-3.f
Normal file
326
Task/Knights-tour/Fortran/knights-tour-3.f
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
!!!
|
||||
!!! Find a Knight’s Tour.
|
||||
!!!
|
||||
!!! Use Warnsdorff’s heuristic, but write the program so it should not
|
||||
!!! be able to terminate unsuccessfully.
|
||||
!!!
|
||||
|
||||
module knights_tour
|
||||
use, intrinsic :: iso_fortran_env, only: output_unit, error_unit
|
||||
|
||||
implicit none
|
||||
private
|
||||
|
||||
public :: find_a_knights_tour
|
||||
public :: notation_is_a_square
|
||||
|
||||
integer, parameter :: number_of_ranks = 8
|
||||
integer, parameter :: number_of_files = 8
|
||||
integer, parameter :: number_of_squares = number_of_ranks * number_of_files
|
||||
|
||||
! ‘Algebraic’ chess notation.
|
||||
character, parameter :: rank_notation(1:8) = (/ '1', '2', '3', '4', '5', '6', '7', '8' /)
|
||||
character, parameter :: file_notation(1:8) = (/ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' /)
|
||||
|
||||
type :: board_square_t
|
||||
! Squares are represented by their algebraic notation.
|
||||
character(2) :: algebraic_notation
|
||||
contains
|
||||
procedure, pass :: output => board_square_t_output
|
||||
procedure, pass :: knight_moves => board_square_t_knight_moves
|
||||
procedure, pass :: equal => board_square_t_equal
|
||||
generic :: operator(==) => equal
|
||||
end type board_square_t
|
||||
|
||||
type :: knight_moves_t
|
||||
integer :: number_of_squares
|
||||
type(board_square_t) :: squares(1:8)
|
||||
end type knight_moves_t
|
||||
|
||||
type :: path_t
|
||||
integer :: length
|
||||
type(board_square_t) :: squares(1:number_of_squares)
|
||||
contains
|
||||
procedure, pass :: output => path_t_output
|
||||
end type path_t
|
||||
|
||||
contains
|
||||
|
||||
pure function notation_is_a_square (notation) result (bool)
|
||||
character(*), intent(in) :: notation
|
||||
logical :: bool
|
||||
|
||||
integer :: length
|
||||
integer :: rank_no
|
||||
integer :: file_no
|
||||
|
||||
length = len_trim (notation)
|
||||
if (length /= 2) then
|
||||
bool = .false.
|
||||
else
|
||||
rank_no = findloc (rank_notation, notation(2:2), 1)
|
||||
file_no = findloc (file_notation, notation(1:1), 1)
|
||||
bool = (1 <= rank_no .and. rank_no <= number_of_ranks) &
|
||||
& .and. (1 <= file_no .and. file_no <= number_of_files)
|
||||
end if
|
||||
end function notation_is_a_square
|
||||
|
||||
subroutine path_t_output (path, unit)
|
||||
!
|
||||
! Print a path in algebraic notation.
|
||||
!
|
||||
class(path_t), intent(in) :: path
|
||||
integer, intent(in) :: unit
|
||||
|
||||
integer :: moves_counter
|
||||
integer :: i
|
||||
|
||||
moves_counter = 1
|
||||
if (1 <= path%length) then
|
||||
call path%squares(1)%output(unit)
|
||||
do i = 2, path%length
|
||||
if (moves_counter == 8) then
|
||||
write (unit, '(" ->")', advance = 'yes')
|
||||
moves_counter = 1
|
||||
else
|
||||
write (unit, '(" -> ")', advance = 'no')
|
||||
moves_counter = moves_counter + 1
|
||||
end if
|
||||
call path%squares(i)%output(unit)
|
||||
end do
|
||||
end if
|
||||
write (output_unit, '()')
|
||||
end subroutine path_t_output
|
||||
|
||||
subroutine board_square_t_output (square, unit)
|
||||
!
|
||||
! Print a square in algebraic notation.
|
||||
!
|
||||
class(board_square_t), intent(in) :: square
|
||||
integer, intent(in) :: unit
|
||||
|
||||
write (unit, '(A2)', advance = 'no') square%algebraic_notation
|
||||
end subroutine board_square_t_output
|
||||
|
||||
elemental function board_square_t_equal (p, q) result (bool)
|
||||
class(board_square_t), intent(in) :: p, q
|
||||
logical :: bool
|
||||
|
||||
bool = (p%algebraic_notation == q%algebraic_notation)
|
||||
end function board_square_t_equal
|
||||
|
||||
pure function board_square_t_knight_moves (square) result (moves)
|
||||
!
|
||||
! Return all possible moves of a knight from a given square.
|
||||
!
|
||||
class(board_square_t), intent(in) :: square
|
||||
type(knight_moves_t) :: moves
|
||||
|
||||
integer, parameter :: rank_stride(1:number_of_ranks) = (/ +1, +2, +1, +2, -1, -2, -1, -2 /)
|
||||
integer, parameter :: file_stride(1:number_of_files) = (/ +2, +1, -2, -1, +2, +1, -2, -1 /)
|
||||
|
||||
integer :: rank_no, file_no
|
||||
integer :: new_rank_no, new_file_no
|
||||
integer :: i
|
||||
character(2) :: notation
|
||||
|
||||
rank_no = findloc (rank_notation, square%algebraic_notation(2:2), 1)
|
||||
file_no = findloc (file_notation, square%algebraic_notation(1:1), 1)
|
||||
|
||||
moves%number_of_squares = 0
|
||||
do i = 1, 8
|
||||
new_rank_no = rank_no + rank_stride(i)
|
||||
new_file_no = file_no + file_stride(i)
|
||||
if (1 <= new_rank_no &
|
||||
& .and. new_rank_no <= number_of_ranks &
|
||||
& .and. 1 <= new_file_no &
|
||||
& .and. new_file_no <= number_of_files) then
|
||||
moves%number_of_squares = moves%number_of_squares + 1
|
||||
notation(2:2) = rank_notation(new_rank_no)
|
||||
notation(1:1) = file_notation(new_file_no)
|
||||
moves%squares(moves%number_of_squares) = board_square_t (notation)
|
||||
end if
|
||||
end do
|
||||
end function board_square_t_knight_moves
|
||||
|
||||
pure function unvisited_knight_moves (path) result (moves)
|
||||
!
|
||||
! Return moves of a knight from a given square, but only those
|
||||
! that have not been visited already.
|
||||
!
|
||||
class(path_t), intent(in) :: path
|
||||
type(knight_moves_t) :: moves
|
||||
|
||||
type(knight_moves_t) :: all_moves
|
||||
integer :: i
|
||||
|
||||
all_moves = path%squares(path%length)%knight_moves()
|
||||
moves%number_of_squares = 0
|
||||
do i = 1, all_moves%number_of_squares
|
||||
if (all (.not. all_moves%squares(i) == path%squares(1:path%length))) then
|
||||
moves%number_of_squares = moves%number_of_squares + 1
|
||||
moves%squares(moves%number_of_squares) = all_moves%squares(i)
|
||||
end if
|
||||
end do
|
||||
end function unvisited_knight_moves
|
||||
|
||||
pure function potential_knight_moves (path) result (moves)
|
||||
!
|
||||
! Return moves of a knight from a given square, but only those
|
||||
! that are unvisited, and from which another unvisited move can be
|
||||
! made.
|
||||
!
|
||||
! Sort the returned moves in nondecreasing order of the number of
|
||||
! possible moves after the first. (This is how we implement
|
||||
! Warnsdorff’s heuristic.)
|
||||
!
|
||||
class(path_t), intent(in) :: path
|
||||
type(knight_moves_t) :: moves
|
||||
|
||||
type(knight_moves_t) :: unvisited_moves
|
||||
type(knight_moves_t) :: next_moves
|
||||
type(path_t) :: next_path
|
||||
type(board_square_t) :: unpruned_squares(1:8)
|
||||
integer :: warnsdorff_numbers(1:8)
|
||||
integer :: number_of_unpruned_squares
|
||||
integer :: i
|
||||
|
||||
if (path%length == number_of_squares - 1) then
|
||||
!
|
||||
! There is only one square left on the board. Either the knight
|
||||
! can reach it or it cannot.
|
||||
!
|
||||
moves = unvisited_knight_moves (path)
|
||||
else
|
||||
!
|
||||
! Use Warnsdorff’s heuristic: return unvisited moves, but try
|
||||
! first those with the least number of possible moves following
|
||||
! it.
|
||||
!
|
||||
! If the number of possible moves following is zero, prune the
|
||||
! move, because it is a dead end.
|
||||
!
|
||||
number_of_unpruned_squares = 0
|
||||
unvisited_moves = unvisited_knight_moves (path)
|
||||
do i = 1, unvisited_moves%number_of_squares
|
||||
next_path%length = path%length + 1
|
||||
next_path%squares(1:path%length) = path%squares(1:path%length)
|
||||
next_path%squares(next_path%length) = unvisited_moves%squares(i)
|
||||
|
||||
next_moves = unvisited_knight_moves (next_path)
|
||||
|
||||
if (next_moves%number_of_squares /= 0) then
|
||||
number_of_unpruned_squares = number_of_unpruned_squares + 1
|
||||
unpruned_squares(number_of_unpruned_squares) = unvisited_moves%squares(i)
|
||||
warnsdorff_numbers(number_of_unpruned_squares) = next_moves%number_of_squares
|
||||
end if
|
||||
end do
|
||||
|
||||
! In-place insertion sort of the unpruned squares.
|
||||
block
|
||||
type(board_square_t) :: square
|
||||
integer :: w_number
|
||||
integer :: i, j
|
||||
|
||||
i = 2
|
||||
do while (i <= number_of_unpruned_squares)
|
||||
square = unpruned_squares(i)
|
||||
w_number = warnsdorff_numbers(i)
|
||||
j = i - 1
|
||||
do while (1 <= j .and. w_number < warnsdorff_numbers(j))
|
||||
unpruned_squares(j + 1) = unpruned_squares(j)
|
||||
warnsdorff_numbers(j + 1) = warnsdorff_numbers(j)
|
||||
j = j - 1
|
||||
end do
|
||||
unpruned_squares(j + 1) = square
|
||||
warnsdorff_numbers(j + 1) = w_number
|
||||
i = i + 1
|
||||
end do
|
||||
end block
|
||||
|
||||
moves%number_of_squares = number_of_unpruned_squares
|
||||
moves%squares(1:number_of_unpruned_squares) = &
|
||||
& unpruned_squares(1:number_of_unpruned_squares)
|
||||
end if
|
||||
end function potential_knight_moves
|
||||
|
||||
subroutine find_a_knights_tour (starting_square)
|
||||
!
|
||||
! Find and print a full knight’s tour.
|
||||
!
|
||||
character(2), intent(in) :: starting_square
|
||||
|
||||
type(path_t) :: path
|
||||
|
||||
path%length = 1
|
||||
path%squares(1) = board_square_t (starting_square)
|
||||
path = try_paths (path)
|
||||
if (path%length /= 0) then
|
||||
call path%output(output_unit)
|
||||
else
|
||||
write (error_unit, '("The program terminated without finding a solution.")')
|
||||
write (error_unit, '("This is supposed to be impossible for an 8-by-8 board.")')
|
||||
write (error_unit, '("The program is wrong.")')
|
||||
error stop
|
||||
end if
|
||||
|
||||
contains
|
||||
|
||||
recursive function try_paths (path) result (solution)
|
||||
!
|
||||
! Recursively try all possible paths, but using Warnsdorff’s
|
||||
! heuristic to speed up the search.
|
||||
!
|
||||
class(path_t), intent(in) :: path
|
||||
type(path_t) :: solution
|
||||
|
||||
type(path_t) :: new_path
|
||||
type(knight_moves_t) :: moves
|
||||
integer :: i
|
||||
|
||||
if (path%length == number_of_squares) then
|
||||
solution = path
|
||||
else
|
||||
solution%length = 0
|
||||
|
||||
moves = potential_knight_moves (path)
|
||||
|
||||
if (moves%number_of_squares /= 0) then
|
||||
new_path%length = path%length + 1
|
||||
new_path%squares(1:path%length) = path%squares(1:path%length)
|
||||
|
||||
i = 1
|
||||
do while (solution%length == 0 .and. i <= moves%number_of_squares)
|
||||
new_path%squares(new_path%length) = moves%squares(i)
|
||||
solution = try_paths (new_path)
|
||||
i = i + 1
|
||||
end do
|
||||
end if
|
||||
end if
|
||||
end function try_paths
|
||||
|
||||
end subroutine find_a_knights_tour
|
||||
|
||||
end module knights_tour
|
||||
|
||||
program knights_tour_main
|
||||
use, intrinsic :: iso_fortran_env, only: output_unit
|
||||
use, non_intrinsic :: knights_tour
|
||||
implicit none
|
||||
|
||||
character(200) :: arg
|
||||
integer :: arg_count
|
||||
integer :: i
|
||||
|
||||
arg_count = command_argument_count ()
|
||||
do i = 1, arg_count
|
||||
call get_command_argument (i, arg)
|
||||
arg = adjustl (arg)
|
||||
if (1 < i) write (output_unit, '()')
|
||||
if (notation_is_a_square (arg)) then
|
||||
call find_a_knights_tour (arg)
|
||||
else
|
||||
write (output_unit, '("This is not algebraic notation: ", A)') arg
|
||||
end if
|
||||
end do
|
||||
end program knights_tour_main
|
||||
57
Task/Knights-tour/FreeBASIC/knights-tour.basic
Normal file
57
Task/Knights-tour/FreeBASIC/knights-tour.basic
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
Dim Shared As Integer tamano, xc, yc, nm
|
||||
Dim As Integer f, qm, nmov, n = 0
|
||||
Dim As String posini
|
||||
|
||||
Cls : Color 11
|
||||
Input "Tamaño tablero: ", tamano
|
||||
Input "Posicion inicial: ", posini
|
||||
|
||||
Dim As Integer x = Asc(Mid(posini,1,1))-96
|
||||
Dim As Integer y = Val(Mid(posini,2,1))
|
||||
Dim Shared As Integer tablero(tamano,tamano), dx(8), dy(8)
|
||||
For f = 1 To 8 : Read dx(f), dy(f) : Next f
|
||||
Data 2,1,1,2,-1,2,-2,1,-2,-1,-1,-2,1,-2,2,-1
|
||||
|
||||
Sub FindMoves()
|
||||
Dim As Integer i, xt, yt
|
||||
If xc < 1 Or yc < 1 Or xc > tamano Or yc > tamano Then nm = 1000: Return
|
||||
If tablero(xc,yc) Then nm = 2000: Return
|
||||
nm = 0
|
||||
For i = 1 To 8
|
||||
xt = xc+dx(i)
|
||||
yt = yc+dy(i)
|
||||
If xt < 1 Or yt < 1 Or xt > tamano Or yt > tamano Then 'Salta este movimiento
|
||||
Elseif tablero(xt,yt) Then 'Salta este movimiento
|
||||
Else
|
||||
nm += 1
|
||||
End If
|
||||
Next i
|
||||
End Sub
|
||||
|
||||
Color 4, 7 'Pinta tablero
|
||||
For f = 1 To tamano
|
||||
Locate 15-tamano, 3*f: Print " "; Chr(96+f); " ";
|
||||
Locate 17-f, 3*(tamano+1)+1: Print Using "##"; f;
|
||||
Next f
|
||||
|
||||
Color 15, 0
|
||||
Do
|
||||
n += 1
|
||||
tablero(x,y) = n
|
||||
Locate 17-y, 3*x: Print Using "###"; n;
|
||||
If n = tamano*tamano Then Exit Do
|
||||
nmov = 100
|
||||
For f = 1 To 8
|
||||
xc = x+dx(f)
|
||||
yc = y+dy(f)
|
||||
FindMoves()
|
||||
If nm < nmov Then nmov = nm: qm = f
|
||||
Next f
|
||||
x = x+dx(qm)
|
||||
y = y+dy(qm)
|
||||
Sleep 1
|
||||
Loop
|
||||
Color 14 : Locate Csrlin+tamano, 1
|
||||
Print " Pulsa cualquier tecla para finalizar..."
|
||||
Sleep
|
||||
End
|
||||
108
Task/Knights-tour/Go/knights-tour-1.go
Normal file
108
Task/Knights-tour/Go/knights-tour-1.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// input, 0-based start position
|
||||
const startRow = 0
|
||||
const startCol = 0
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
for !knightTour() {
|
||||
}
|
||||
}
|
||||
|
||||
var moves = []struct{ dr, dc int }{
|
||||
{2, 1},
|
||||
{2, -1},
|
||||
{1, 2},
|
||||
{1, -2},
|
||||
{-1, 2},
|
||||
{-1, -2},
|
||||
{-2, 1},
|
||||
{-2, -1},
|
||||
}
|
||||
|
||||
// Attempt knight tour starting at startRow, startCol using Warnsdorff's rule
|
||||
// and random tie breaking. If a tour is found, print it and return true.
|
||||
// Otherwise no backtracking, just return false.
|
||||
func knightTour() bool {
|
||||
// 8x8 board. squares hold 1-based visit order. 0 means unvisited.
|
||||
board := make([][]int, 8)
|
||||
for i := range board {
|
||||
board[i] = make([]int, 8)
|
||||
}
|
||||
r := startRow
|
||||
c := startCol
|
||||
board[r][c] = 1 // first move
|
||||
for move := 2; move <= 64; move++ {
|
||||
minNext := 8
|
||||
var mr, mc, nm int
|
||||
candidateMoves:
|
||||
for _, cm := range moves {
|
||||
cr := r + cm.dr
|
||||
if cr < 0 || cr >= 8 { // off board
|
||||
continue
|
||||
}
|
||||
cc := c + cm.dc
|
||||
if cc < 0 || cc >= 8 { // off board
|
||||
continue
|
||||
}
|
||||
if board[cr][cc] > 0 { // already visited
|
||||
continue
|
||||
}
|
||||
// cr, cc candidate legal move.
|
||||
p := 0 // count possible next moves.
|
||||
for _, m2 := range moves {
|
||||
r2 := cr + m2.dr
|
||||
if r2 < 0 || r2 >= 8 {
|
||||
continue
|
||||
}
|
||||
c2 := cc + m2.dc
|
||||
if c2 < 0 || c2 >= 8 {
|
||||
continue
|
||||
}
|
||||
if board[r2][c2] > 0 {
|
||||
continue
|
||||
}
|
||||
p++
|
||||
if p > minNext { // bail out as soon as it's eliminated
|
||||
continue candidateMoves
|
||||
}
|
||||
}
|
||||
if p < minNext { // it's better. keep it.
|
||||
minNext = p // new min possible next moves
|
||||
nm = 1 // number of candidates with this p
|
||||
mr = cr // best candidate move
|
||||
mc = cc
|
||||
continue
|
||||
}
|
||||
// it ties for best so far.
|
||||
// keep it with probability 1/(number of tying moves)
|
||||
nm++ // number of tying moves
|
||||
if rand.Intn(nm) == 0 { // one chance to keep it
|
||||
mr = cr
|
||||
mc = cc
|
||||
}
|
||||
}
|
||||
if nm == 0 { // no legal move
|
||||
return false
|
||||
}
|
||||
// make selected move
|
||||
r = mr
|
||||
c = mc
|
||||
board[r][c] = move
|
||||
}
|
||||
// tour complete. print board.
|
||||
for _, r := range board {
|
||||
for _, m := range r {
|
||||
fmt.Printf("%3d", m)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return true
|
||||
}
|
||||
192
Task/Knights-tour/Go/knights-tour-2.go
Normal file
192
Task/Knights-tour/Go/knights-tour-2.go
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/* Adapted from "Enumerating Knight's Tours using an Ant Colony Algorithm"
|
||||
by Philip Hingston and Graham Kendal,
|
||||
PDF at http://www.cs.nott.ac.uk/~gxk/papers/cec05knights.pdf. */
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const boardSize = 8
|
||||
const nSquares = boardSize * boardSize
|
||||
const completeTour = nSquares - 1
|
||||
|
||||
// task input: starting square. These are 1 based, but otherwise 0 based
|
||||
// row and column numbers are used througout the program.
|
||||
const rStart = 2
|
||||
const cStart = 3
|
||||
|
||||
// pheromone representation read by ants
|
||||
var tNet = make([]float64, nSquares*8)
|
||||
|
||||
// row, col deltas of legal moves
|
||||
var drc = [][]int{{1, 2}, {2, 1}, {2, -1}, {1, -2},
|
||||
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}}
|
||||
|
||||
// get square reached by following edge k from square (r, c)
|
||||
func dest(r, c, k int) (int, int, bool) {
|
||||
r += drc[k][0]
|
||||
c += drc[k][1]
|
||||
return r, c, r >= 0 && r < boardSize && c >= 0 && c < boardSize
|
||||
}
|
||||
|
||||
// struct represents a pheromone amount associated with a move
|
||||
type rckt struct {
|
||||
r, c, k int
|
||||
t float64
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Starting square: row", rStart, "column", cStart)
|
||||
// initialize board
|
||||
for r := 0; r < boardSize; r++ {
|
||||
for c := 0; c < boardSize; c++ {
|
||||
for k := 0; k < 8; k++ {
|
||||
if _, _, ok := dest(r, c, k); ok {
|
||||
tNet[(r*boardSize+c)*8+k] = 1e-6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// waitGroups for ant release clockwork
|
||||
var start, reset sync.WaitGroup
|
||||
start.Add(1)
|
||||
// channel for ants to return tours with pheremone updates
|
||||
tch := make(chan []rckt)
|
||||
|
||||
// create an ant for each square
|
||||
for r := 0; r < boardSize; r++ {
|
||||
for c := 0; c < boardSize; c++ {
|
||||
go ant(r, c, &start, &reset, tch)
|
||||
}
|
||||
}
|
||||
|
||||
// accumulator for new pheromone amounts
|
||||
tNew := make([]float64, nSquares*8)
|
||||
|
||||
// each iteration is a "cycle" as described in the paper
|
||||
for {
|
||||
// evaporate pheromones
|
||||
for i := range tNet {
|
||||
tNet[i] *= .75
|
||||
}
|
||||
|
||||
reset.Add(nSquares) // number of ants to release
|
||||
start.Done() // release them
|
||||
reset.Wait() // wait for them to begin searching
|
||||
start.Add(1) // reset start signal for next cycle
|
||||
|
||||
// gather tours from ants
|
||||
for i := 0; i < nSquares; i++ {
|
||||
tour := <-tch
|
||||
// watch for a complete tour from the specified starting square
|
||||
if len(tour) == completeTour &&
|
||||
tour[0].r == rStart-1 && tour[0].c == cStart-1 {
|
||||
|
||||
// task output: move sequence in a grid.
|
||||
seq := make([]int, nSquares)
|
||||
for i, sq := range tour {
|
||||
seq[sq.r*boardSize+sq.c] = i + 1
|
||||
}
|
||||
last := tour[len(tour)-1]
|
||||
r, c, _ := dest(last.r, last.c, last.k)
|
||||
seq[r*boardSize+c] = nSquares
|
||||
fmt.Println("Move sequence:")
|
||||
for r := 0; r < boardSize; r++ {
|
||||
for c := 0; c < boardSize; c++ {
|
||||
fmt.Printf(" %3d", seq[r*boardSize+c])
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return // task only requires finding a single tour
|
||||
}
|
||||
// accumulate pheromone amounts from all ants
|
||||
for _, move := range tour {
|
||||
tNew[(move.r*boardSize+move.c)*8+move.k] += move.t
|
||||
}
|
||||
}
|
||||
|
||||
// update pheromone amounts on network, reset accumulator
|
||||
for i, tn := range tNew {
|
||||
tNet[i] += tn
|
||||
tNew[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type square struct {
|
||||
r, c int
|
||||
}
|
||||
|
||||
func ant(r, c int, start, reset *sync.WaitGroup, tourCh chan []rckt) {
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
tabu := make([]square, nSquares)
|
||||
moves := make([]rckt, nSquares)
|
||||
unexp := make([]rckt, 8)
|
||||
tabu[0].r = r
|
||||
tabu[0].c = c
|
||||
|
||||
for {
|
||||
// cycle initialization
|
||||
moves = moves[:0]
|
||||
tabu = tabu[:1]
|
||||
r := tabu[0].r
|
||||
c := tabu[0].c
|
||||
|
||||
// wait for start signal
|
||||
start.Wait()
|
||||
reset.Done()
|
||||
|
||||
for {
|
||||
// choose next move
|
||||
unexp = unexp[:0]
|
||||
var tSum float64
|
||||
findU:
|
||||
for k := 0; k < 8; k++ {
|
||||
dr, dc, ok := dest(r, c, k)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, t := range tabu {
|
||||
if t.r == dr && t.c == dc {
|
||||
continue findU
|
||||
}
|
||||
}
|
||||
tk := tNet[(r*boardSize+c)*8+k]
|
||||
tSum += tk
|
||||
// note: dest r, c stored here
|
||||
unexp = append(unexp, rckt{dr, dc, k, tk})
|
||||
}
|
||||
if len(unexp) == 0 {
|
||||
break // no moves
|
||||
}
|
||||
rn := rnd.Float64() * tSum
|
||||
var move rckt
|
||||
for _, move = range unexp {
|
||||
if rn <= move.t {
|
||||
break
|
||||
}
|
||||
rn -= move.t
|
||||
}
|
||||
|
||||
// move to new square
|
||||
move.r, r = r, move.r
|
||||
move.c, c = c, move.c
|
||||
tabu = append(tabu, square{r, c})
|
||||
moves = append(moves, move)
|
||||
}
|
||||
|
||||
// compute pheromone amount to leave
|
||||
for i := range moves {
|
||||
moves[i].t = float64(len(moves)-i) / float64(completeTour-i)
|
||||
}
|
||||
|
||||
// return tour found for this cycle
|
||||
tourCh <- moves
|
||||
}
|
||||
}
|
||||
62
Task/Knights-tour/Haskell/knights-tour.hs
Normal file
62
Task/Knights-tour/Haskell/knights-tour.hs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import Data.Bifunctor (bimap)
|
||||
import Data.Char (chr, ord)
|
||||
import Data.List (intercalate, minimumBy, sort, (\\))
|
||||
import Data.Ord (comparing)
|
||||
import Control.Monad (join)
|
||||
|
||||
---------------------- KNIGHT'S TOUR ---------------------
|
||||
|
||||
type Square = (Int, Int)
|
||||
|
||||
knightTour :: [Square] -> [Square]
|
||||
knightTour moves
|
||||
| null possibilities = reverse moves
|
||||
| otherwise = knightTour $ newSquare : moves
|
||||
where
|
||||
newSquare =
|
||||
minimumBy
|
||||
(comparing (length . findMoves))
|
||||
possibilities
|
||||
possibilities = findMoves $ head moves
|
||||
findMoves = (\\ moves) . knightOptions
|
||||
|
||||
knightOptions :: Square -> [Square]
|
||||
knightOptions (x, y) =
|
||||
knightMoves >>= go . bimap (+ x) (+ y)
|
||||
where
|
||||
go move
|
||||
| uncurry (&&) (both onBoard move) = [move]
|
||||
| otherwise = []
|
||||
|
||||
knightMoves :: [(Int, Int)]
|
||||
knightMoves =
|
||||
((>>=) <*> (\deltas n -> deltas >>= go n)) [1, 2, -1, -2]
|
||||
where
|
||||
go i x
|
||||
| abs i /= abs x = [(i, x)]
|
||||
| otherwise = []
|
||||
|
||||
onBoard :: Int -> Bool
|
||||
onBoard = (&&) . (0 <) <*> (9 >)
|
||||
|
||||
both :: (a -> b) -> (a, a) -> (b, b)
|
||||
both = join bimap
|
||||
|
||||
--------------------------- TEST -------------------------
|
||||
startPoint :: String
|
||||
startPoint = "e5"
|
||||
|
||||
algebraic :: (Int, Int) -> String
|
||||
algebraic (x, y) = [chr (x + 96), chr (y + 48)]
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
printTour $
|
||||
algebraic
|
||||
<$> knightTour
|
||||
[(\[x, y] -> (ord x - 96, ord y - 48)) startPoint]
|
||||
where
|
||||
printTour [] = return ()
|
||||
printTour tour = do
|
||||
putStrLn $ intercalate " -> " $ take 8 tour
|
||||
printTour $ drop 8 tour
|
||||
101
Task/Knights-tour/Icon/knights-tour-1.icon
Normal file
101
Task/Knights-tour/Icon/knights-tour-1.icon
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
link printf
|
||||
|
||||
procedure main(A)
|
||||
ShowTour(KnightsTour(Board(8)))
|
||||
end
|
||||
|
||||
procedure KnightsTour(B,sq,tbrk,debug) #: Warnsdorff’s algorithm
|
||||
|
||||
/B := Board(8) # create 8x8 board if none given
|
||||
/sq := ?B.files || ?B.ranks # random initial position (default)
|
||||
sq2fr(sq,B) # validate initial sq
|
||||
if type(tbrk) == "procedure" then
|
||||
B.tiebreak := tbrk # override tie-breaker
|
||||
if \debug then write("Debug log : move#, move : (accessibility) choices")
|
||||
|
||||
choices := [] # setup to track moves and choices
|
||||
every (movesto := table())[k := key(B.movesto)] := copy(B.movesto[k])
|
||||
|
||||
B.tour := [] # new tour
|
||||
repeat {
|
||||
put(B.tour,sq) # record move
|
||||
|
||||
ac := 9 # accessibility counter > maximum
|
||||
while get(choices) # empty choices for tiebreak
|
||||
every delete(movesto[nextsq := !movesto[sq]],sq) do { # make sq unavailable
|
||||
if ac >:= *movesto[nextsq] then # reset to lower accessibility count
|
||||
while get(choices) # . re-empty choices
|
||||
if ac = *movesto[nextsq] then
|
||||
put(choices,nextsq) # keep least accessible sq and any ties
|
||||
}
|
||||
|
||||
if \debug then { # move#, move, (accessibility), choices
|
||||
writes(sprintf("%d. %s : (%d) ",*B.tour,sq,ac))
|
||||
every writes(" ",!choices|"\n")
|
||||
}
|
||||
sq := B.tiebreak(choices,B) | break # choose next sq until out of choices
|
||||
}
|
||||
return B
|
||||
end
|
||||
|
||||
procedure RandomTieBreaker(S,B) # random choice
|
||||
return ?S
|
||||
end
|
||||
|
||||
procedure FirstTieBreaker(S,B) # first one in the list
|
||||
return !S
|
||||
end
|
||||
|
||||
procedure RothTieBreaker(S,B) # furthest from the center
|
||||
if *S = 0 then fail # must fail if []
|
||||
every fr := sq2fr(s := !S,B) do {
|
||||
d := sqrt(abs(fr[1]-1 - (B.N-1)*0.5)^2 + abs(fr[2]-1 - (B.N-1)*0.5)^2)
|
||||
if (/md := d) | ( md >:= d) then msq := s # save sq
|
||||
}
|
||||
return msq
|
||||
end
|
||||
|
||||
record board(N,ranks,files,movesto,tiebreak,tour) # structure for board
|
||||
|
||||
procedure Board(N) #: create board
|
||||
N := *&lcase >=( 0 < integer(N)) | stop("N=",image(N)," is out of range.")
|
||||
B := board(N,[],&lcase[1+:N],table(),RandomTieBreaker) # setup
|
||||
every put(B.ranks,N to 1 by -1) # add rank #s
|
||||
every sq := !B.files || !B.ranks do # for each sq add
|
||||
every insert(B.movesto[sq] := set(), KnightMoves(sq,B)) # moves to next sq
|
||||
return B
|
||||
end
|
||||
|
||||
procedure sq2fr(sq,B) #: return numeric file & rank
|
||||
f := find(sq[1],B.files) | runerr(205,sq)
|
||||
r := integer(B.ranks[sq[2:0]]) | runerr(205,sq)
|
||||
return [f,r]
|
||||
end
|
||||
|
||||
procedure KnightMoves(sq,B) #: generate all Kn accessible moves from sq
|
||||
fr := sq2fr(sq,B)
|
||||
every ( i := -2|-1|1|2 ) & ( j := -2|-1|1|2 ) do
|
||||
if (abs(i)~=abs(j)) & (0<(ri:=fr[2]+i)<=B.N) & (0<(fj:=fr[1]+j)<=B.N) then
|
||||
suspend B.files[fj]||B.ranks[ri]
|
||||
end
|
||||
|
||||
procedure ShowTour(B) #: show the tour
|
||||
write("Board size = ",B.N)
|
||||
write("Tour length = ",*B.tour)
|
||||
write("Tie Breaker = ",image(B.tiebreak))
|
||||
|
||||
every !(squares := list(B.N)) := list(B.N,"-")
|
||||
every fr := sq2fr(B.tour[m := 1 to *B.tour],B) do
|
||||
squares[fr[2],fr[1]] := m
|
||||
|
||||
every (hdr1 := " ") ||:= right(!B.files,3)
|
||||
every (hdr2 := " +") ||:= repl((1 to B.N,"-"),3) | "-+"
|
||||
|
||||
every write(hdr1|hdr2)
|
||||
every r := 1 to B.N do {
|
||||
writes(right(B.ranks[r],3)," |")
|
||||
every writes(right(squares[r,f := 1 to B.N],3))
|
||||
write(" |",right(B.ranks[r],3))
|
||||
}
|
||||
every write(hdr2|hdr1|&null)
|
||||
end
|
||||
11
Task/Knights-tour/Icon/knights-tour-2.icon
Normal file
11
Task/Knights-tour/Icon/knights-tour-2.icon
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
procedure DumpBoard(B) #: Dump Board internals
|
||||
write("Board size=",B.N)
|
||||
write("Available Moves at start of tour:", ImageMovesTo(B.movesto))
|
||||
end
|
||||
|
||||
procedure ImageMovesTo(movesto) #: image of available moves
|
||||
every put(K := [],key(movesto))
|
||||
every (s := "\n") ||:= (k := !sort(K)) || " : " do
|
||||
every s ||:= " " || (!sort(movesto[k])|"\n")
|
||||
return s
|
||||
end
|
||||
17
Task/Knights-tour/J/knights-tour-1.j
Normal file
17
Task/Knights-tour/J/knights-tour-1.j
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
NB. knight moves for each square of a (y,y) board
|
||||
kmoves=: monad define
|
||||
t=. (>,{;~i.y) +"1/ _2]\2 1 2 _1 1 2 1 _2 _1 2 _1 _2 _2 1 _2 _1
|
||||
(*./"1 t e. i.y) <@#"1 y#.t
|
||||
)
|
||||
|
||||
ktourw=: monad define
|
||||
M=. >kmoves y
|
||||
p=. k=. 0
|
||||
b=. 1 $~ *:y
|
||||
for. i.<:*:y do.
|
||||
b=. 0 k}b
|
||||
p=. p,k=. ((i.<./) +/"1 b{~j{M){j=. ({&b # ]) k{M
|
||||
end.
|
||||
assert. ~:p
|
||||
(,~y)$/:p
|
||||
)
|
||||
22
Task/Knights-tour/J/knights-tour-2.j
Normal file
22
Task/Knights-tour/J/knights-tour-2.j
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
ktourw 8 NB. solution for an 8 x 8 board
|
||||
0 25 14 23 28 49 12 31
|
||||
15 22 27 50 13 30 63 48
|
||||
26 1 24 29 62 59 32 11
|
||||
21 16 51 58 43 56 47 60
|
||||
2 41 20 55 52 61 10 33
|
||||
17 38 53 42 57 44 7 46
|
||||
40 3 36 19 54 5 34 9
|
||||
37 18 39 4 35 8 45 6
|
||||
|
||||
9!:37]0 64 4 4 NB. truncate lines longer than 64 characters and only show first and last four lines
|
||||
|
||||
ktourw 202 NB. 202x202 board -- this implementation failed for 200 and 201
|
||||
0 401 414 405 398 403 424 417 396 419 43...
|
||||
413 406 399 402 425 416 397 420 439 430 39...
|
||||
400 1 426 415 404 423 448 429 418 437 4075...
|
||||
409 412 407 446 449 428 421 440 40739 40716 43...
|
||||
...
|
||||
550 99 560 569 9992 779 786 773 10002 9989 78...
|
||||
555 558 553 778 563 570 775 780 785 772 1000...
|
||||
100 551 556 561 102 777 572 771 104 781 57...
|
||||
557 554 101 552 571 562 103 776 573 770 10...
|
||||
98
Task/Knights-tour/Java/knights-tour-1.java
Normal file
98
Task/Knights-tour/Java/knights-tour-1.java
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import java.util.*;
|
||||
|
||||
public class KnightsTour {
|
||||
private final static int base = 12;
|
||||
private final static int[][] moves = {{1,-2},{2,-1},{2,1},{1,2},{-1,2},
|
||||
{-2,1},{-2,-1},{-1,-2}};
|
||||
private static int[][] grid;
|
||||
private static int total;
|
||||
|
||||
public static void main(String[] args) {
|
||||
grid = new int[base][base];
|
||||
total = (base - 4) * (base - 4);
|
||||
|
||||
for (int r = 0; r < base; r++)
|
||||
for (int c = 0; c < base; c++)
|
||||
if (r < 2 || r > base - 3 || c < 2 || c > base - 3)
|
||||
grid[r][c] = -1;
|
||||
|
||||
int row = 2 + (int) (Math.random() * (base - 4));
|
||||
int col = 2 + (int) (Math.random() * (base - 4));
|
||||
|
||||
grid[row][col] = 1;
|
||||
|
||||
if (solve(row, col, 2))
|
||||
printResult();
|
||||
else System.out.println("no result");
|
||||
|
||||
}
|
||||
|
||||
private static boolean solve(int r, int c, int count) {
|
||||
if (count > total)
|
||||
return true;
|
||||
|
||||
List<int[]> nbrs = neighbors(r, c);
|
||||
|
||||
if (nbrs.isEmpty() && count != total)
|
||||
return false;
|
||||
|
||||
Collections.sort(nbrs, new Comparator<int[]>() {
|
||||
public int compare(int[] a, int[] b) {
|
||||
return a[2] - b[2];
|
||||
}
|
||||
});
|
||||
|
||||
for (int[] nb : nbrs) {
|
||||
r = nb[0];
|
||||
c = nb[1];
|
||||
grid[r][c] = count;
|
||||
if (!orphanDetected(count, r, c) && solve(r, c, count + 1))
|
||||
return true;
|
||||
grid[r][c] = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<int[]> neighbors(int r, int c) {
|
||||
List<int[]> nbrs = new ArrayList<>();
|
||||
|
||||
for (int[] m : moves) {
|
||||
int x = m[0];
|
||||
int y = m[1];
|
||||
if (grid[r + y][c + x] == 0) {
|
||||
int num = countNeighbors(r + y, c + x);
|
||||
nbrs.add(new int[]{r + y, c + x, num});
|
||||
}
|
||||
}
|
||||
return nbrs;
|
||||
}
|
||||
|
||||
private static int countNeighbors(int r, int c) {
|
||||
int num = 0;
|
||||
for (int[] m : moves)
|
||||
if (grid[r + m[1]][c + m[0]] == 0)
|
||||
num++;
|
||||
return num;
|
||||
}
|
||||
|
||||
private static boolean orphanDetected(int cnt, int r, int c) {
|
||||
if (cnt < total - 1) {
|
||||
List<int[]> nbrs = neighbors(r, c);
|
||||
for (int[] nb : nbrs)
|
||||
if (countNeighbors(nb[0], nb[1]) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void printResult() {
|
||||
for (int[] row : grid) {
|
||||
for (int i : row) {
|
||||
if (i == -1) continue;
|
||||
System.out.printf("%2d ", i);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Task/Knights-tour/Java/knights-tour-2.java
Normal file
158
Task/Knights-tour/Java/knights-tour-2.java
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package com.knight.tour;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class KT {
|
||||
|
||||
private int baseSize = 12; // virtual board size including unreachable out-of-board nodes. i.e. base 12 = 8X8 board
|
||||
int actualBoardSize = baseSize - 4;
|
||||
private static final int[][] moves = { { 1, -2 }, { 2, -1 }, { 2, 1 }, { 1, 2 }, { -1, 2 }, { -2, 1 }, { -2, -1 },
|
||||
{ -1, -2 } };
|
||||
private static int[][] grid;
|
||||
private static int totalNodes;
|
||||
private ArrayList<int[]> travelledNodes = new ArrayList<>();
|
||||
public KT(int baseNumber) {
|
||||
this.baseSize = baseNumber;
|
||||
this.actualBoardSize = baseSize - 4;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new KT(12).tour(); // find a solution for 8X8 board
|
||||
// new KT(24).tour(); // then for 20X20 board
|
||||
// new KT(104).tour(); // then for 100X100 board
|
||||
}
|
||||
|
||||
private void tour() {
|
||||
totalNodes = actualBoardSize * actualBoardSize;
|
||||
travelledNodes.clear();
|
||||
grid = new int[baseSize][baseSize];
|
||||
for (int r = 0; r < baseSize; r++)
|
||||
for (int c = 0; c < baseSize; c++) {
|
||||
if (r < 2 || r > baseSize - 3 || c < 2 || c > baseSize - 3) {
|
||||
grid[r][c] = -1; // mark as out-of-board nodes
|
||||
} else {
|
||||
grid[r][c] = 0; // nodes within chess board.
|
||||
}
|
||||
}
|
||||
// start from a random node
|
||||
int startRow = 2 + (int) (Math.random() * actualBoardSize);
|
||||
int startCol = 2 + (int) (Math.random() * actualBoardSize);
|
||||
int[] start = { startRow, startCol, 0, 1 };
|
||||
grid[startRow][startCol] = 1; // mark the first traveled node
|
||||
travelledNodes.add(start); // add to partial solution chain, which will only have one node.
|
||||
|
||||
// Start traveling forward
|
||||
autoKnightTour(start, 2);
|
||||
}
|
||||
|
||||
// non-backtracking touring methods. Re-chain the partial solution when all neighbors are traveled to avoid back-tracking.
|
||||
private void autoKnightTour(int[] start, int nextCount) {
|
||||
List<int[]> nbrs = neighbors(start[0], start[1]);
|
||||
if (nbrs.size() > 0) {
|
||||
Collections.sort(nbrs, new Comparator<int[]>() {
|
||||
public int compare(int[] a, int[] b) {
|
||||
return a[2] - b[2];
|
||||
}
|
||||
}); // sort the list
|
||||
int[] next = nbrs.get(0); // the one with the less available neighbors - Warnsdorff's algorithm
|
||||
next[3] = nextCount;
|
||||
travelledNodes.add(next);
|
||||
grid[next[0]][next[1]] = nextCount;
|
||||
if (travelledNodes.size() == totalNodes) {
|
||||
System.out.println("Found a path for " + actualBoardSize + " X " + actualBoardSize + " chess board.");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(System.lineSeparator());
|
||||
for (int idx = 0; idx < travelledNodes.size(); idx++) {
|
||||
int[] item = travelledNodes.get(idx);
|
||||
sb.append("->(" + (item[0] - 2) + "," + (item[1] - 2) + ")");
|
||||
if ((idx + 1) % 15 == 0) {
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
System.out.println(sb.toString() + "\n");
|
||||
} else { // continuing the travel
|
||||
autoKnightTour(next, ++nextCount);
|
||||
}
|
||||
} else { // no travelable neighbors next - need to rechain the partial chain
|
||||
int[] last = travelledNodes.get(travelledNodes.size() - 1);
|
||||
travelledNodes = reChain(travelledNodes);
|
||||
if (travelledNodes.get(travelledNodes.size() - 1).equals(last)) {
|
||||
travelledNodes = reChain(travelledNodes);
|
||||
if (travelledNodes.get(travelledNodes.size() - 1).equals(last)) {
|
||||
System.out.println("Re-chained twice but no travllable node found. Quiting...");
|
||||
} else {
|
||||
int[] end = travelledNodes.get(travelledNodes.size() - 1);
|
||||
autoKnightTour(end, nextCount);
|
||||
}
|
||||
} else {
|
||||
int[] end = travelledNodes.get(travelledNodes.size() - 1);
|
||||
autoKnightTour(end, nextCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<int[]> reChain(ArrayList<int[]> alreadyTraveled) {
|
||||
int[] last = alreadyTraveled.get(alreadyTraveled.size() - 1);
|
||||
List<int[]> candidates = neighborsInChain(last[0], last[1]);
|
||||
int cutIndex;
|
||||
int[] randomPicked = candidates.get((int) Math.random() * candidates.size());
|
||||
cutIndex = grid[randomPicked[0]][randomPicked[1]] - 1;
|
||||
ArrayList<int[]> result = new ArrayList<int[]>(); //create empty list to copy already traveled nodes to
|
||||
for (int k = 0; k <= cutIndex; k++) {
|
||||
result.add(result.size(), alreadyTraveled.get(k));
|
||||
}
|
||||
for (int j = alreadyTraveled.size() - 1; j > cutIndex; j--) {
|
||||
alreadyTraveled.get(j)[3] = result.size();
|
||||
result.add(result.size(), alreadyTraveled.get(j));
|
||||
}
|
||||
return result; // re-chained partial solution with different end node
|
||||
}
|
||||
|
||||
private List<int[]> neighborsInChain(int r, int c) {
|
||||
List<int[]> nbrs = new ArrayList<>();
|
||||
for (int[] m : moves) {
|
||||
int x = m[0];
|
||||
int y = m[1];
|
||||
if (grid[r + y][c + x] > 0 && grid[r + y][c + x] != grid[r][c] - 1) {
|
||||
int num = countNeighbors(r + y, c + x);
|
||||
nbrs.add(new int[] { r + y, c + x, num, 0 });
|
||||
}
|
||||
}
|
||||
return nbrs;
|
||||
}
|
||||
|
||||
private static List<int[]> neighbors(int r, int c) {
|
||||
List<int[]> nbrs = new ArrayList<>();
|
||||
for (int[] m : moves) {
|
||||
int x = m[0];
|
||||
int y = m[1];
|
||||
if (grid[r + y][c + x] == 0) {
|
||||
int num = countNeighbors(r + y, c + x);
|
||||
nbrs.add(new int[] { r + y, c + x, num, 0 }); // not-traveled neighbors and number of their neighbors
|
||||
}
|
||||
}
|
||||
return nbrs;
|
||||
|
||||
}
|
||||
|
||||
private List<int[]> extendableNeighbors(List<int[]> neighbors) {
|
||||
List<int[]> nbrs = new ArrayList<>();
|
||||
for (int[] node : neighbors) {
|
||||
if (node[2] > 0)
|
||||
nbrs.add(node);
|
||||
}
|
||||
return nbrs;
|
||||
}
|
||||
|
||||
private static int countNeighbors(int r, int c) {
|
||||
int num = 0;
|
||||
for (int[] m : moves) {
|
||||
if (grid[r + m[1]][c + m[0]] == 0) {
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
215
Task/Knights-tour/JavaScript/knights-tour-1.js
Normal file
215
Task/Knights-tour/JavaScript/knights-tour-1.js
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
class KnightTour {
|
||||
constructor() {
|
||||
this.width = 856;
|
||||
this.height = 856;
|
||||
this.cellCount = 8;
|
||||
this.size = 0;
|
||||
this.knightPiece = "\u2658";
|
||||
this.knightPos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
this.ctx = null;
|
||||
this.step = this.width / this.cellCount;
|
||||
this.lastTime = 0;
|
||||
this.wait;
|
||||
this.delay;
|
||||
this.success;
|
||||
this.jumps;
|
||||
this.directions = [];
|
||||
this.visited = [];
|
||||
this.path = [];
|
||||
document.getElementById("start").addEventListener("click", () => {
|
||||
this.startHtml();
|
||||
});
|
||||
this.init();
|
||||
this.drawBoard();
|
||||
}
|
||||
|
||||
drawBoard() {
|
||||
let a = false, xx, yy;
|
||||
for (let y = 0; y < this.cellCount; y++) {
|
||||
for (let x = 0; x < this.cellCount; x++) {
|
||||
if (a) {
|
||||
this.ctx.fillStyle = "#607db8";
|
||||
} else {
|
||||
this.ctx.fillStyle = "#aecaf0";
|
||||
}
|
||||
a = !a;
|
||||
xx = x * this.step;
|
||||
yy = y * this.step;
|
||||
this.ctx.fillRect(xx, yy, xx + this.step, yy + this.step);
|
||||
}
|
||||
if (!(this.cellCount & 1)) a = !a;
|
||||
}
|
||||
if (this.path.length) {
|
||||
const s = this.step >> 1;
|
||||
this.ctx.lineWidth = 3;
|
||||
this.ctx.fillStyle = "black";
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.step * this.knightPos.x + s, this.step * this.knightPos.y + s);
|
||||
let a, b, v = this.path.length - 1;
|
||||
for (; v > -1; v--) {
|
||||
a = this.path[v].pos.x * this.step + s;
|
||||
b = this.path[v].pos.y * this.step + s;
|
||||
this.ctx.lineTo(a, b);
|
||||
this.ctx.fillRect(a - 5, b - 5, 10, 10);
|
||||
}
|
||||
this.ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
createMoves(pos) {
|
||||
const possibles = [];
|
||||
let x = 0,
|
||||
y = 0,
|
||||
m = 0,
|
||||
l = this.directions.length;
|
||||
for (; m < l; m++) {
|
||||
x = pos.x + this.directions[m].x;
|
||||
y = pos.y + this.directions[m].y;
|
||||
if (x > -1 && x < this.cellCount && y > -1 && y < this.cellCount && !this.visited[x + y * this.cellCount]) {
|
||||
possibles.push({
|
||||
x,
|
||||
y
|
||||
})
|
||||
}
|
||||
}
|
||||
return possibles;
|
||||
}
|
||||
|
||||
warnsdorff(pos) {
|
||||
const possibles = this.createMoves(pos);
|
||||
if (possibles.length < 1) return [];
|
||||
const moves = [];
|
||||
for (let p = 0, l = possibles.length; p < l; p++) {
|
||||
let ps = this.createMoves(possibles[p]);
|
||||
moves.push({
|
||||
len: ps.length,
|
||||
pos: possibles[p]
|
||||
});
|
||||
}
|
||||
moves.sort((a, b) => {
|
||||
return b.len - a.len;
|
||||
});
|
||||
return moves;
|
||||
}
|
||||
|
||||
startHtml() {
|
||||
this.cellCount = parseInt(document.getElementById("cellCount").value);
|
||||
this.size = Math.floor(this.width / this.cellCount)
|
||||
this.wait = this.delay = parseInt(document.getElementById("delay").value);
|
||||
this.step = this.width / this.cellCount;
|
||||
this.ctx.font = this.size + "px Arial";
|
||||
document.getElementById("log").innerText = "";
|
||||
document.getElementById("path").innerText = "";
|
||||
this.path = [];
|
||||
this.jumps = 1;
|
||||
this.success = true;
|
||||
this.visited = [];
|
||||
const cnt = this.cellCount * this.cellCount;
|
||||
for (let a = 0; a < cnt; a++) {
|
||||
this.visited.push(false);
|
||||
}
|
||||
const kx = parseInt(document.getElementById("knightx").value),
|
||||
ky = parseInt(document.getElementById("knighty").value);
|
||||
this.knightPos = {
|
||||
x: (kx > this.cellCount || kx < 0) ? Math.floor(Math.random() * this.cellCount) : kx,
|
||||
y: (ky > this.cellCount || ky < 0) ? Math.floor(Math.random() * this.cellCount) : ky
|
||||
};
|
||||
this.mainLoop = (time = 0) => {
|
||||
const dif = time - this.lastTime;
|
||||
this.lastTime = time;
|
||||
this.wait -= dif;
|
||||
if (this.wait > 0) {
|
||||
requestAnimationFrame(this.mainLoop);
|
||||
return;
|
||||
}
|
||||
this.wait = this.delay;
|
||||
let moves;
|
||||
if (this.success) {
|
||||
moves = this.warnsdorff(this.knightPos);
|
||||
} else {
|
||||
if (this.path.length > 0) {
|
||||
const path = this.path[this.path.length - 1];
|
||||
moves = path.m;
|
||||
if (moves.length < 1) this.path.pop();
|
||||
this.knightPos = path.pos
|
||||
this.visited[this.knightPos.x + this.knightPos.y * this.cellCount] = false;
|
||||
this.jumps--;
|
||||
this.wait = this.delay;
|
||||
} else {
|
||||
document.getElementById("log").innerText = "Can't find a solution!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.drawBoard();
|
||||
const ft = this.step - (this.step >> 3);
|
||||
this.ctx.fillStyle = "#000";
|
||||
this.ctx.fillText(this.knightPiece, this.knightPos.x * this.step, this.knightPos.y * this.step + ft);
|
||||
if (moves.length < 1) {
|
||||
if (this.jumps === this.cellCount * this.cellCount) {
|
||||
document.getElementById("log").innerText = "Tour finished!";
|
||||
let str = "";
|
||||
for (let z of this.path) {
|
||||
str += `${1 + z.pos.x + z.pos.y * this.cellCount}, `;
|
||||
}
|
||||
str += `${1 + this.knightPos.x + this.knightPos.y * this.cellCount}`;
|
||||
document.getElementById("path").innerText = str;
|
||||
return;
|
||||
} else {
|
||||
this.success = false;
|
||||
}
|
||||
} else {
|
||||
this.visited[this.knightPos.x + this.knightPos.y * this.cellCount] = true;
|
||||
const move = moves.pop();
|
||||
this.path.push({
|
||||
pos: this.knightPos,
|
||||
m: moves
|
||||
});
|
||||
this.knightPos = move.pos
|
||||
this.success = true;
|
||||
this.jumps++;
|
||||
}
|
||||
requestAnimationFrame(this.mainLoop);
|
||||
};
|
||||
this.mainLoop();
|
||||
}
|
||||
|
||||
init() {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.id = "cv";
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
this.ctx = canvas.getContext("2d");
|
||||
document.getElementById("out").appendChild(canvas);
|
||||
this.directions = [{
|
||||
x: -1,
|
||||
y: -2
|
||||
}, {
|
||||
x: -2,
|
||||
y: -1
|
||||
}, {
|
||||
x: 1,
|
||||
y: -2
|
||||
}, {
|
||||
x: 2,
|
||||
y: -1
|
||||
},
|
||||
{
|
||||
x: -1,
|
||||
y: 2
|
||||
}, {
|
||||
x: -2,
|
||||
y: 1
|
||||
}, {
|
||||
x: 1,
|
||||
y: 2
|
||||
}, {
|
||||
x: 2,
|
||||
y: 1
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
new KnightTour();
|
||||
294
Task/Knights-tour/JavaScript/knights-tour-2.js
Normal file
294
Task/Knights-tour/JavaScript/knights-tour-2.js
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
(() => {
|
||||
'use strict';
|
||||
|
||||
// knightsTour :: Int -> [(Int, Int)] -> [(Int, Int)]
|
||||
const knightsTour = rowLength => moves => {
|
||||
const go = path => {
|
||||
const
|
||||
findMoves = xy => difference(knightMoves(xy), path),
|
||||
warnsdorff = minimumBy(
|
||||
comparing(compose(length, findMoves))
|
||||
),
|
||||
options = findMoves(path[0]);
|
||||
return 0 < options.length ? (
|
||||
go([warnsdorff(options)].concat(path))
|
||||
) : reverse(path);
|
||||
};
|
||||
|
||||
// board :: [[(Int, Int)]]
|
||||
const board = concatMap(
|
||||
col => concatMap(
|
||||
row => [
|
||||
[col, row]
|
||||
],
|
||||
enumFromTo(1, rowLength)),
|
||||
enumFromTo(1, rowLength)
|
||||
);
|
||||
|
||||
// knightMoves :: (Int, Int) -> [(Int, Int)]
|
||||
const knightMoves = ([x, y]) =>
|
||||
concatMap(
|
||||
([dx, dy]) => {
|
||||
const ab = [x + dx, y + dy];
|
||||
return elem(ab, board) ? (
|
||||
[ab]
|
||||
) : [];
|
||||
}, [
|
||||
[-2, -1],
|
||||
[-2, 1],
|
||||
[-1, -2],
|
||||
[-1, 2],
|
||||
[1, -2],
|
||||
[1, 2],
|
||||
[2, -1],
|
||||
[2, 1]
|
||||
]
|
||||
);
|
||||
return go(moves);
|
||||
};
|
||||
|
||||
// TEST -----------------------------------------------
|
||||
// main :: IO()
|
||||
const main = () => {
|
||||
|
||||
// boardSize :: Int
|
||||
const boardSize = 8;
|
||||
|
||||
// tour :: [(Int, Int)]
|
||||
const tour = knightsTour(boardSize)(
|
||||
[fromAlgebraic('e5')]
|
||||
);
|
||||
|
||||
// report :: String
|
||||
const report = '(Board size ' +
|
||||
boardSize + '*' + boardSize + ')\n\n' +
|
||||
'Route: \n\n' +
|
||||
showRoute(boardSize)(tour) + '\n\n' +
|
||||
'Coverage and order: \n\n' +
|
||||
showCoverage(boardSize)(tour) + '\n\n';
|
||||
return (
|
||||
console.log(report),
|
||||
report
|
||||
);
|
||||
}
|
||||
|
||||
// DISPLAY --------------------------------------------
|
||||
|
||||
// algebraic :: (Int, Int) -> String
|
||||
const algebraic = ([x, y]) =>
|
||||
chr(x + 96) + y.toString();
|
||||
|
||||
// fromAlgebraic :: String -> (Int, Int)
|
||||
const fromAlgebraic = s =>
|
||||
2 <= s.length ? (
|
||||
[ord(s[0]) - 96, parseInt(s.slice(1))]
|
||||
) : undefined;
|
||||
|
||||
// showCoverage :: Int -> [(Int, Int)] -> String
|
||||
const showCoverage = rowLength => xys => {
|
||||
const
|
||||
intMax = xys.length,
|
||||
w = 1 + intMax.toString().length
|
||||
return unlines(map(concat,
|
||||
chunksOf(
|
||||
rowLength,
|
||||
map(composeList([justifyRight(w, ' '), str, fst]),
|
||||
sortBy(
|
||||
mappendComparing([
|
||||
compose(fst, snd),
|
||||
compose(snd, snd)
|
||||
]),
|
||||
zip(enumFromTo(1, intMax), xys)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
};
|
||||
|
||||
// showRoute :: Int -> [(Int, Int)] -> String
|
||||
const showRoute = rowLength => xys => {
|
||||
const w = 1 + rowLength.toString().length;
|
||||
return unlines(map(
|
||||
xs => xs.join(' -> '),
|
||||
chunksOf(
|
||||
rowLength,
|
||||
map(compose(justifyRight(w, ' '), algebraic), xys)
|
||||
)
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
// GENERIC FUNCTIONS ----------------------------------
|
||||
|
||||
|
||||
// Tuple (,) :: a -> b -> (a, b)
|
||||
const Tuple = (a, b) => ({
|
||||
type: 'Tuple',
|
||||
'0': a,
|
||||
'1': b,
|
||||
length: 2
|
||||
});
|
||||
|
||||
// chr :: Int -> Char
|
||||
const chr = x => String.fromCodePoint(x);
|
||||
|
||||
// chunksOf :: Int -> [a] -> [[a]]
|
||||
const chunksOf = (n, xs) =>
|
||||
enumFromThenTo(0, n, xs.length - 1)
|
||||
.reduce(
|
||||
(a, i) => a.concat([xs.slice(i, (n + i))]),
|
||||
[]
|
||||
);
|
||||
|
||||
// compare :: a -> a -> Ordering
|
||||
const compare = (a, b) =>
|
||||
a < b ? -1 : (a > b ? 1 : 0);
|
||||
|
||||
// comparing :: (a -> b) -> (a -> a -> Ordering)
|
||||
const comparing = f =>
|
||||
(x, y) => {
|
||||
const
|
||||
a = f(x),
|
||||
b = f(y);
|
||||
return a < b ? -1 : (a > b ? 1 : 0);
|
||||
};
|
||||
|
||||
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
||||
const compose = (f, g) => x => f(g(x));
|
||||
|
||||
// composeList :: [(a -> a)] -> (a -> a)
|
||||
const composeList = fs =>
|
||||
x => fs.reduceRight((a, f) => f(a), x, fs);
|
||||
|
||||
// concat :: [[a]] -> [a]
|
||||
// concat :: [String] -> String
|
||||
const concat = xs =>
|
||||
0 < xs.length ? (() => {
|
||||
const unit = 'string' !== typeof xs[0] ? (
|
||||
[]
|
||||
) : '';
|
||||
return unit.concat.apply(unit, xs);
|
||||
})() : [];
|
||||
|
||||
// concatMap :: (a -> [b]) -> [a] -> [b]
|
||||
const concatMap = (f, xs) =>
|
||||
xs.reduce((a, x) => a.concat(f(x)), []);
|
||||
|
||||
|
||||
// difference :: Eq a => [a] -> [a] -> [a]
|
||||
const difference = (xs, ys) => {
|
||||
const s = new Set(ys.map(str));
|
||||
return xs.filter(x => !s.has(str(x)));
|
||||
};
|
||||
|
||||
// elem :: Eq a => a -> [a] -> Bool
|
||||
const elem = (x, xs) => xs.some(eq(x))
|
||||
|
||||
|
||||
// enumFromThenTo :: Int -> Int -> Int -> [Int]
|
||||
const enumFromThenTo = (x1, x2, y) => {
|
||||
const d = x2 - x1;
|
||||
return Array.from({
|
||||
length: Math.floor(y - x2) / d + 2
|
||||
}, (_, i) => x1 + (d * i));
|
||||
};
|
||||
|
||||
// enumFromTo :: Int -> Int -> [Int]
|
||||
const enumFromTo = (m, n) =>
|
||||
Array.from({
|
||||
length: 1 + n - m
|
||||
}, (_, i) => m + i);
|
||||
|
||||
// eq (==) :: Eq a => a -> a -> Bool
|
||||
const eq = a => b => {
|
||||
const t = typeof a;
|
||||
return t !== typeof b ? (
|
||||
false
|
||||
) : 'object' !== t ? (
|
||||
'function' !== t ? (
|
||||
a === b
|
||||
) : a.toString() === b.toString()
|
||||
) : (() => {
|
||||
const kvs = Object.entries(a);
|
||||
return kvs.length !== Object.keys(b).length ? (
|
||||
false
|
||||
) : kvs.every(([k, v]) => eq(v)(b[k]));
|
||||
})();
|
||||
};
|
||||
|
||||
// fst :: (a, b) -> a
|
||||
const fst = tpl => tpl[0];
|
||||
|
||||
// justifyRight :: Int -> Char -> String -> String
|
||||
const justifyRight = (n, cFiller) => s =>
|
||||
n > s.length ? (
|
||||
s.padStart(n, cFiller)
|
||||
) : s;
|
||||
|
||||
|
||||
// length :: [a] -> Int
|
||||
const length = xs =>
|
||||
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
||||
xs.length
|
||||
) : Infinity;
|
||||
|
||||
// map :: (a -> b) -> [a] -> [b]
|
||||
const map = (f, xs) =>
|
||||
(Array.isArray(xs) ? (
|
||||
xs
|
||||
) : xs.split('')).map(f);
|
||||
|
||||
// mappendComparing :: [(a -> b)] -> (a -> a -> Ordering)
|
||||
const mappendComparing = fs =>
|
||||
(x, y) => fs.reduce(
|
||||
(ordr, f) => (ordr || compare(f(x), f(y))),
|
||||
0
|
||||
);
|
||||
|
||||
// minimumBy :: (a -> a -> Ordering) -> [a] -> a
|
||||
const minimumBy = f => xs =>
|
||||
xs.reduce((a, x) => undefined === a ? x : (
|
||||
0 > f(x, a) ? x : a
|
||||
), undefined);
|
||||
|
||||
// ord :: Char -> Int
|
||||
const ord = c => c.codePointAt(0);
|
||||
|
||||
// reverse :: [a] -> [a]
|
||||
const reverse = xs =>
|
||||
'string' !== typeof xs ? (
|
||||
xs.slice(0).reverse()
|
||||
) : xs.split('').reverse().join('');
|
||||
|
||||
// snd :: (a, b) -> b
|
||||
const snd = tpl => tpl[1];
|
||||
|
||||
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
|
||||
const sortBy = (f, xs) =>
|
||||
xs.slice()
|
||||
.sort(f);
|
||||
|
||||
// str :: a -> String
|
||||
const str = x => x.toString();
|
||||
|
||||
// take :: Int -> [a] -> [a]
|
||||
// take :: Int -> String -> String
|
||||
const take = (n, xs) =>
|
||||
xs.slice(0, n);
|
||||
|
||||
// unlines :: [String] -> String
|
||||
const unlines = xs => xs.join('\n');
|
||||
|
||||
// Use of `take` and `length` here allows for zipping with non-finite
|
||||
// lists - i.e. generators like cycle, repeat, iterate.
|
||||
|
||||
// zip :: [a] -> [b] -> [(a, b)]
|
||||
const zip = (xs, ys) => {
|
||||
const lng = Math.min(length(xs), length(ys));
|
||||
const bs = take(lng, ys);
|
||||
return take(lng, xs).map((x, i) => Tuple(x, bs[i]));
|
||||
};
|
||||
|
||||
// MAIN ---
|
||||
return main();
|
||||
})();
|
||||
18
Task/Knights-tour/Julia/knights-tour.julia
Normal file
18
Task/Knights-tour/Julia/knights-tour.julia
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using .Hidato # Note that the . here means to look locally for the module rather than in the libraries
|
||||
|
||||
const chessboard = """
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0 """
|
||||
|
||||
const knightmoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]
|
||||
|
||||
board, maxmoves, fixed, starts = hidatoconfigure(chessboard)
|
||||
printboard(board, " 0", " ")
|
||||
hidatosolve(board, maxmoves, knightmoves, fixed, starts[1][1], starts[1][2], 1)
|
||||
printboard(board)
|
||||
29
Task/Knights-tour/Kotlin/knights-tour.kotlin
Normal file
29
Task/Knights-tour/Kotlin/knights-tour.kotlin
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
data class Square(val x : Int, val y : Int)
|
||||
|
||||
val board = Array(8 * 8, { Square(it / 8 + 1, it % 8 + 1) })
|
||||
val axisMoves = arrayOf(1, 2, -1, -2)
|
||||
|
||||
fun <T> allPairs(a: Array<T>) = a.flatMap { i -> a.map { j -> Pair(i, j) } }
|
||||
|
||||
fun knightMoves(s : Square) : List<Square> {
|
||||
val moves = allPairs(axisMoves).filter{ Math.abs(it.first) != Math.abs(it.second) }
|
||||
fun onBoard(s : Square) = board.any {it == s}
|
||||
return moves.map { Square(s.x + it.first, s.y + it.second) }.filter(::onBoard)
|
||||
}
|
||||
|
||||
fun knightTour(moves : List<Square>) : List<Square> {
|
||||
fun findMoves(s: Square) = knightMoves(s).filterNot { m -> moves.any { it == m } }
|
||||
val newSquare = findMoves(moves.last()).minBy { findMoves(it).size }
|
||||
return if (newSquare == null) moves else knightTour(moves + newSquare)
|
||||
}
|
||||
|
||||
fun knightTourFrom(start : Square) = knightTour(listOf(start))
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
var col = 0
|
||||
for ((x, y) in knightTourFrom(Square(1, 1))) {
|
||||
System.out.print("$x,$y")
|
||||
System.out.print(if (col == 7) "\n" else " ")
|
||||
col = (col + 1) % 8
|
||||
}
|
||||
}
|
||||
47
Task/Knights-tour/Locomotive-Basic/knights-tour.basic
Normal file
47
Task/Knights-tour/Locomotive-Basic/knights-tour.basic
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
10 mode 1:defint a-z
|
||||
20 input "Board size: ",size
|
||||
30 input "Start position: ",a$
|
||||
40 x=asc(mid$(a$,1,1))-96
|
||||
50 y=val(mid$(a$,2,1))
|
||||
60 dim play(size,size)
|
||||
70 for q=1 to 8
|
||||
80 read dx(q),dy(q)
|
||||
90 next
|
||||
100 data 2,1,1,2,-1,2,-2,1,-2,-1,-1,-2,1,-2,2,-1
|
||||
110 pen 0:paper 1
|
||||
120 for q=1 to size
|
||||
130 locate 3*q+1,24-size
|
||||
140 print chr$(96+q);
|
||||
150 locate 3*(size+1)+1,26-q
|
||||
160 print using "#"; q;
|
||||
170 next
|
||||
180 pen 1:paper 0
|
||||
190 ' main loop
|
||||
200 n=n+1
|
||||
210 play(x,y)=n
|
||||
220 locate 3*x,26-y
|
||||
230 print using "##"; n;
|
||||
240 if n=size*size then call &bb06:end
|
||||
250 nmov=100
|
||||
260 for q=1 to 8
|
||||
270 xc=x+dx(q)
|
||||
280 yc=y+dy(q)
|
||||
290 gosub 360
|
||||
300 if nm<nmov then nmov=nm:qm=q
|
||||
310 next
|
||||
320 x=x+dx(qm)
|
||||
330 y=y+dy(qm)
|
||||
340 goto 200
|
||||
350 ' find moves
|
||||
360 if xc<1 or yc<1 or xc>size or yc>size then nm=1000:return
|
||||
370 if play(xc,yc) then nm=2000:return
|
||||
380 nm=0
|
||||
390 for q2=1 to 8
|
||||
400 xt=xc+dx(q2)
|
||||
410 yt=yc+dy(q2)
|
||||
420 if xt<1 or yt<1 or xt>size or yt>size then 460
|
||||
430 if play(xt,yt) then 460
|
||||
440 nm=nm+1
|
||||
450 ' skip this move
|
||||
460 next
|
||||
470 return
|
||||
51
Task/Knights-tour/Lua/knights-tour.lua
Normal file
51
Task/Knights-tour/Lua/knights-tour.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
N = 8
|
||||
|
||||
moves = { {1,-2},{2,-1},{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2} }
|
||||
|
||||
function Move_Allowed( board, x, y )
|
||||
if board[x][y] >= 8 then return false end
|
||||
|
||||
local new_x, new_y = x + moves[board[x][y]+1][1], y + moves[board[x][y]+1][2]
|
||||
if new_x >= 1 and new_x <= N and new_y >= 1 and new_y <= N and board[new_x][new_y] == 0 then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
board = {}
|
||||
for i = 1, N do
|
||||
board[i] = {}
|
||||
for j = 1, N do
|
||||
board[i][j] = 0
|
||||
end
|
||||
end
|
||||
|
||||
x, y = 1, 1
|
||||
|
||||
lst = {}
|
||||
lst[1] = { x, y }
|
||||
|
||||
repeat
|
||||
if Move_Allowed( board, x, y ) then
|
||||
board[x][y] = board[x][y] + 1
|
||||
x, y = x+moves[board[x][y]][1], y+moves[board[x][y]][2]
|
||||
lst[#lst+1] = { x, y }
|
||||
else
|
||||
if board[x][y] >= 8 then
|
||||
board[x][y] = 0
|
||||
lst[#lst] = nil
|
||||
if #lst == 0 then
|
||||
print "No solution found."
|
||||
os.exit(1)
|
||||
end
|
||||
x, y = lst[#lst][1], lst[#lst][2]
|
||||
end
|
||||
board[x][y] = board[x][y] + 1
|
||||
end
|
||||
until #lst == N^2
|
||||
|
||||
last = lst[1]
|
||||
for i = 2, #lst do
|
||||
print( string.format( "%s%d - %s%d", string.sub("ABCDEFGH",last[1],last[1]), last[2], string.sub("ABCDEFGH",lst[i][1],lst[i][1]), lst[i][2] ) )
|
||||
last = lst[i]
|
||||
end
|
||||
102
Task/Knights-tour/M2000-Interpreter/knights-tour.m2000
Normal file
102
Task/Knights-tour/M2000-Interpreter/knights-tour.m2000
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
Function KnightTour$(StartW=1, StartH=1){
|
||||
def boolean swapH, swapV=True
|
||||
if startW<=4 then swapH=true: StartW=8+1-StartW
|
||||
if startH>4 then swapV=False: StartH=8+1-StartH
|
||||
Let final=8*8, last=final-1, HighValue=final+1
|
||||
Dim Board(1 to 8, 1 to 8), Moves(1 to 8, 1 to 8)=HighValue
|
||||
f=stack:=1,2,3,4,5,6,7,8
|
||||
if 8-StartW=2 and StartH=2 then stack f {shift 1,-8}
|
||||
Function KnightMove(x,w,h) {
|
||||
a=2:b=1:z=1:p=1
|
||||
if x mod 2=1 then swap a,b
|
||||
if x>2 then p-! : if x>4 then swap z, p : if x>6 then p-!
|
||||
w+=z*a
|
||||
h+=p*b
|
||||
if w>=1 and w<=8 and h>=1 and h<=8 then =(w, h) else =(,)
|
||||
}
|
||||
For j=1 to 8 :For i=1 to 8
|
||||
s=stack
|
||||
For k=1 to 8
|
||||
m=KnightMove(stackitem(f, k),i, j)
|
||||
if len(m)>1 then Stack s {data m}
|
||||
Next : Board(i,j)=s : Next
|
||||
stack f {shift 1,-8}
|
||||
Next
|
||||
For i=1 to 8 :For j=1 to 8
|
||||
s=Board(i, j)
|
||||
if len(s)>2 then
|
||||
so=queue
|
||||
For k=1 to len(s)
|
||||
m=stackitem(s, k)
|
||||
Append so, Len(Board(m#val(0), m#val(1))) :=m
|
||||
Next
|
||||
sort ascending so as number
|
||||
s=stack
|
||||
stack s {for k=0 to len(so)-1:data so(k!):next}
|
||||
Board(i,j)=s
|
||||
end if
|
||||
Next : Next
|
||||
s= Board(StartW, StartH)
|
||||
n=0
|
||||
BackTrack=Stack
|
||||
Moves=1
|
||||
Moves(StartW, StartH)=1
|
||||
Repeat
|
||||
n++
|
||||
While n>len(s) {
|
||||
if Len(BackTrack)=0 then Print "Break", moves : Break
|
||||
Moves--
|
||||
Stack BackTrack {Read s, n}
|
||||
m=stackitem(s, n)
|
||||
Moves(m#val(0), m#val(1))=HighValue
|
||||
n++
|
||||
}
|
||||
m=stackitem(s, n)
|
||||
w=m#val(0)
|
||||
h=m#val(1)
|
||||
if Moves(w, h)>=Moves then
|
||||
if Moves<last then
|
||||
s1=Board(w, h) :ii=-1
|
||||
for i=1 to len(s1){m1=stackitem(s1, i) :if Moves(m1#val(0),m1#val(1))>moves then ii=i-1 : exit
|
||||
}
|
||||
if ii>=0 then
|
||||
Moves++
|
||||
Moves(w,h)=Moves
|
||||
Stack BackTrack {Push n, s}
|
||||
s=s1: n=ii
|
||||
end if
|
||||
else
|
||||
Moves++
|
||||
Moves(w,h)=Moves
|
||||
end if
|
||||
end if
|
||||
until Moves=final
|
||||
Document export$
|
||||
Inventory Tour
|
||||
letters=stack:="a","b","c","d","e","f","g","h"
|
||||
f=stack:=1,2,3,4,5,6,7,8
|
||||
if swapV Else stack f {Shift 1,-8}
|
||||
if swapH then stack letters {Shift 1,-8}
|
||||
For j=1 to 8:For i=1 to 8
|
||||
Append Tour, Moves(i,j) :=stackitem$(letters, i)+str$(stackitem(f, j),"")
|
||||
Next : Next
|
||||
Sort ascending Tour as number
|
||||
one=each(Tour)
|
||||
While one {
|
||||
export$=Eval$(one)
|
||||
if not one^=last then export$="->"
|
||||
If (one^+1) mod 8=0 then
|
||||
export$={
|
||||
}
|
||||
End if
|
||||
}
|
||||
=export$
|
||||
}
|
||||
Document ex$
|
||||
ex$= {Knight's Tour from a1
|
||||
}+KnightTour$()+{Knight's Tour from h1
|
||||
}+KnightTour$(8,1)+{Knight's Tour from a8
|
||||
}+KnightTour$(1, 8)+{Knight's Tour from h8
|
||||
}+KnightTour$(8, 8)
|
||||
Clipboard ex$
|
||||
Report ex$
|
||||
179
Task/Knights-tour/M4/knights-tour.m4
Normal file
179
Task/Knights-tour/M4/knights-tour.m4
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
divert(-1)
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
This is free and unencumbered software released into the public
|
||||
domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Find a Knight's tour, via Warnsdorff's rule.
|
||||
|
||||
For very old or 'Heirloom' m4, you may need to increase the sizes of
|
||||
internal structures, with, say,
|
||||
|
||||
m4 -S 1000 -B 100000 knights_tour.m4
|
||||
|
||||
But I would use one of OpenBSD m4, GNU m4, etc., instead.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
dnl Get a random number from 0 to one less than $1.
|
||||
dnl (Note that this is not a very good RNG. Also it writes a file.)
|
||||
define(`randnum',
|
||||
`syscmd(`echo $RANDOM > __random_number__')eval(include(__random_number__) % ( $1 ))')
|
||||
|
||||
|
||||
dnl The left deconstructors for strings.
|
||||
define(`string_car',`substr($1,0,1)')
|
||||
define(`string_cdr',`substr($1,1)')
|
||||
|
||||
dnl Algebraic notation to 'i0j0', with i the ranks and j the files. Bad
|
||||
dnl algebraic notation gets tranformed to '99999999'.
|
||||
define(`alg2ij',
|
||||
`ifelse($1,`a1',`1010',$1,`a2',`2010',$1,`a3',`3010',$1,`a4',`4010',
|
||||
$1,`a5',`5010',$1,`a6',`6010',$1,`a7',`7010',$1,`a8',`8010',
|
||||
$1,`b1',`1020',$1,`b2',`2020',$1,`b3',`3020',$1,`b4',`4020',
|
||||
$1,`b5',`5020',$1,`b6',`6020',$1,`b7',`7020',$1,`b8',`8020',
|
||||
$1,`c1',`1030',$1,`c2',`2030',$1,`c3',`3030',$1,`c4',`4030',
|
||||
$1,`c5',`5030',$1,`c6',`6030',$1,`c7',`7030',$1,`c8',`8030',
|
||||
$1,`d1',`1040',$1,`d2',`2040',$1,`d3',`3040',$1,`d4',`4040',
|
||||
$1,`d5',`5040',$1,`d6',`6040',$1,`d7',`7040',$1,`d8',`8040',
|
||||
$1,`e1',`1050',$1,`e2',`2050',$1,`e3',`3050',$1,`e4',`4050',
|
||||
$1,`e5',`5050',$1,`e6',`6050',$1,`e7',`7050',$1,`e8',`8050',
|
||||
$1,`f1',`1060',$1,`f2',`2060',$1,`f3',`3060',$1,`f4',`4060',
|
||||
$1,`f5',`5060',$1,`f6',`6060',$1,`f7',`7060',$1,`f8',`8060',
|
||||
$1,`g1',`1070',$1,`g2',`2070',$1,`g3',`3070',$1,`g4',`4070',
|
||||
$1,`g5',`5070',$1,`g6',`6070',$1,`g7',`7070',$1,`g8',`8070',
|
||||
$1,`h1',`1080',$1,`h2',`2080',$1,`h3',`3080',$1,`h4',`4080',
|
||||
$1,`h5',`5080',$1,`h6',`6080',$1,`h7',`7080',$1,`h8',`8080',
|
||||
`99999999')')
|
||||
|
||||
dnl The reverse of alg2ij. Bad 'i0j0' get transformed to 'z0'.
|
||||
define(`ij2alg',
|
||||
`ifelse($1,`1010',`a1',$1,`2010',`a2',$1,`3010',`a3',$1,`4010',`a4',
|
||||
$1,`5010',`a5',$1,`6010',`a6',$1,`7010',`a7',$1,`8010',`a8',
|
||||
$1,`1020',`b1',$1,`2020',`b2',$1,`3020',`b3',$1,`4020',`b4',
|
||||
$1,`5020',`b5',$1,`6020',`b6',$1,`7020',`b7',$1,`8020',`b8',
|
||||
$1,`1030',`c1',$1,`2030',`c2',$1,`3030',`c3',$1,`4030',`c4',
|
||||
$1,`5030',`c5',$1,`6030',`c6',$1,`7030',`c7',$1,`8030',`c8',
|
||||
$1,`1040',`d1',$1,`2040',`d2',$1,`3040',`d3',$1,`4040',`d4',
|
||||
$1,`5040',`d5',$1,`6040',`d6',$1,`7040',`d7',$1,`8040',`d8',
|
||||
$1,`1050',`e1',$1,`2050',`e2',$1,`3050',`e3',$1,`4050',`e4',
|
||||
$1,`5050',`e5',$1,`6050',`e6',$1,`7050',`e7',$1,`8050',`e8',
|
||||
$1,`1060',`f1',$1,`2060',`f2',$1,`3060',`f3',$1,`4060',`f4',
|
||||
$1,`5060',`f5',$1,`6060',`f6',$1,`7060',`f7',$1,`8060',`f8',
|
||||
$1,`1070',`g1',$1,`2070',`g2',$1,`3070',`g3',$1,`4070',`g4',
|
||||
$1,`5070',`g5',$1,`6070',`g6',$1,`7070',`g7',$1,`8070',`g8',
|
||||
$1,`1080',`h1',$1,`2080',`h2',$1,`3080',`h3',$1,`4080',`h4',
|
||||
$1,`5080',`h5',$1,`6080',`h6',$1,`7080',`h7',$1,`8080',`h8',
|
||||
`z0')')
|
||||
|
||||
dnl Move a knight from one square to another by an ij-vector. Both input
|
||||
dnl and output are algebraic notation. If the move is illegal, it comes
|
||||
dnl out as 'z0'.
|
||||
define(`move_by',`ij2alg(eval(alg2ij($3) + 1000 * ( $1 ) + 10 * ( $2 )))')
|
||||
|
||||
dnl For example, a1d3c5 -> 3
|
||||
define(`path_length',`eval(len($1) / 2)')
|
||||
|
||||
dnl The left deconstructors for paths.
|
||||
define(`path_car',`substr($1,0,2)')
|
||||
define(`path_cdr',`substr($1,2)')
|
||||
|
||||
dnl The right deconstructors for paths.
|
||||
define(`path_last',`substr($1,eval(len($1) - 2))')
|
||||
define(`path_drop_last',`substr($1,0,eval(len($1) - 2))')
|
||||
|
||||
dnl Extract the nth position from the path.
|
||||
define(`path_nth',`substr($1,eval(( $2 ) * 2),2)')
|
||||
|
||||
define(`random_move',`path_nth($1,randnum(path_length($1)))')
|
||||
|
||||
dnl Is the position $1 contained in the path $2?
|
||||
define(`path_contains',`ifelse(index($2,$1),-1,0,1)')
|
||||
|
||||
dnl Find all moves from position $1 that are not already in
|
||||
dnl the path $2.
|
||||
define(`possible_moves',
|
||||
`ifelse(path_contains(move_by(1,2,$1),$2`'z0),`0',move_by(1,2,$1))`'dnl
|
||||
ifelse(path_contains(move_by(2,1,$1),$2`'z0),`0',move_by(2,1,$1))`'dnl
|
||||
ifelse(path_contains(move_by(1,-2,$1),$2`'z0),`0',move_by(1,-2,$1))`'dnl
|
||||
ifelse(path_contains(move_by(2,-1,$1),$2`'z0),`0',move_by(2,-1,$1))`'dnl
|
||||
ifelse(path_contains(move_by(-1,2,$1),$2`'z0),`0',move_by(-1,2,$1))`'dnl
|
||||
ifelse(path_contains(move_by(-2,1,$1),$2`'z0),`0',move_by(-2,1,$1))`'dnl
|
||||
ifelse(path_contains(move_by(-1,-2,$1),$2`'z0),`0',move_by(-1,-2,$1))`'dnl
|
||||
ifelse(path_contains(move_by(-2,-1,$1),$2`'z0),`0',move_by(-2,-1,$1))')
|
||||
|
||||
dnl Count how many moves can follow each move in $1.
|
||||
define(`follows_counts',
|
||||
`ifelse($1,`',`',
|
||||
`path_length(possible_moves(path_car($1),$2))`'follows_counts(path_cdr($1),$2)')')
|
||||
|
||||
dnl Find the smallest positive digit, or zero.
|
||||
define(`min_positive',
|
||||
`ifelse($1,`',0,
|
||||
`pushdef(`min1',min_positive(string_cdr($1)))`'dnl
|
||||
pushdef(`val1',string_car($1))`'dnl
|
||||
ifelse(min1,0,val1,
|
||||
val1,0,min1,
|
||||
eval(val1 < min1),1,val1,min1)`'dnl
|
||||
popdef(`min1',`val1')')')
|
||||
|
||||
dnl Change everything to zero that is not the minimum positive.
|
||||
define(`apply_warnsdorff',`_$0(min_positive($1),$1)')
|
||||
define(`_apply_warnsdorff',
|
||||
`ifelse($2,`',`',`ifelse(string_car($2),$1,$1,0)`'$0($1,string_cdr($2))')')
|
||||
|
||||
dnl Find potential next moves that satisfy Warnsdorff's rule.
|
||||
define(`warnsdorff_moves',
|
||||
`pushdef(`moves',`possible_moves($1,$2)')`'dnl
|
||||
pushdef(`selections',`apply_warnsdorff(follows_counts(moves))')`'dnl
|
||||
_$0(moves,selections)`'dnl
|
||||
popdef(`moves',`selections')')
|
||||
define(`_warnsdorff_moves',
|
||||
`ifelse($1,`',`',
|
||||
`ifelse(string_car($2),0,`$0(path_cdr($1),string_cdr($2))',
|
||||
`path_car($1)`'$0(path_cdr($1),string_cdr($2))')')')
|
||||
|
||||
dnl Find potential next moves for the given path.
|
||||
define(`next_moves',
|
||||
`ifelse(path_length($1),63,`possible_moves(path_last($1),$1)',
|
||||
`warnsdorff_moves(path_last($1),$1)')')
|
||||
|
||||
define(`find_tour',
|
||||
`ifelse($2,`',`find_tour($1,$1)',
|
||||
path_length($2),64,$2,
|
||||
`pushdef(`moves',next_moves($2))`'dnl
|
||||
ifelse(moves,`',`find_tour($1)',
|
||||
`find_tour($1,$2`'random_move(next_moves($2)))')`'dnl
|
||||
popdef(`moves')')')
|
||||
|
||||
divert`'dnl
|
||||
dnl
|
||||
find_tour(a1)
|
||||
find_tour(c5)
|
||||
find_tour(h8)
|
||||
8
Task/Knights-tour/Mathematica/knights-tour-1.math
Normal file
8
Task/Knights-tour/Mathematica/knights-tour-1.math
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
knightsTourMoves[start_] :=
|
||||
Module[{
|
||||
vertexLabels = (# -> ToString@c[[Quotient[# - 1, 8] + 1]] <> ToString[Mod[# - 1, 8] + 1]) & /@ Range[64], knightsGraph,
|
||||
hamiltonianCycle, end},
|
||||
knightsGraph = KnightTourGraph[i, i, VertexLabels -> vertexLabels, ImagePadding -> 15];
|
||||
hamiltonianCycle = ((FindHamiltonianCycle[knightsGraph] /. UndirectedEdge -> DirectedEdge) /. labels)[[1]];
|
||||
end = Cases[hamiltonianCycle, (x_ \[DirectedEdge] start) :> x][[1]];
|
||||
FindShortestPath[g, start, end]]
|
||||
6
Task/Knights-tour/Mathematica/knights-tour-2.math
Normal file
6
Task/Knights-tour/Mathematica/knights-tour-2.math
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
knightsTourMoves["d8"]
|
||||
|
||||
(* out *)
|
||||
{"d8", "e6", "d4", "c2", "a1", "b3", "a5", "b7", "c5", "a4", "b2", "c4", "a3", "b1", "c3", "a2", "b4", "a6", "b8", "c6", "a7", "b5", \
|
||||
"c7", "a8", "b6", "c8", "d6", "e4", "d2", "f1", "e3", "d1", "f2", "h1", "g3", "e2", "c1", "d3", "e1", "g2", "h4", "f5", "e7", "d5", \
|
||||
"f4", "h5", "g7", "e8", "f6", "g8", "h6", "g4", "h2", "f3", "g1", "h3", "g5", "h7", "f8", "d7", "e5", "g6", "h8", "f7"}
|
||||
11
Task/Knights-tour/Mathematica/knights-tour-3.math
Normal file
11
Task/Knights-tour/Mathematica/knights-tour-3.math
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
vertexLabels = (# -> ToString@c[[Quotient[# - 1, 8] + 1]] <> ToString[Mod[# - 1, 8] + 1]) & /@ Range[64]
|
||||
|
||||
(* out *)
|
||||
{1 -> "a1", 2 -> "a2", 3 -> "a3", 4 -> "a4", 5 -> "a5", 6 -> "a6", 7 -> "a7", 8 -> "a8",
|
||||
9 -> "b1", 10 -> "b2", 11 -> "b3", 12 -> "b4", 13 -> "b5", 14 -> "b6", 15 -> "b7", 16 -> "b8",
|
||||
17 -> "c1", 18 -> "c2", 19 -> "c3", 20 -> "c4", 21 -> "c5", 22 -> "c6", 23 -> "c7", 24 -> "c8",
|
||||
25 -> "d1", 26 -> "d2", 27 -> "d3", 28 -> "d4", 29 -> "d5", 30 -> "d6", 31 -> "d7", 32 -> "d8",
|
||||
33 -> "e1", 34 -> "e2", 35 -> "e3", 36 -> "e4", 37 -> "e5", 38 -> "e6", 39 -> "e7", 40 -> "e8",
|
||||
41 -> "f1", 42 -> "f2", 43 -> "f3", 44 -> "f4", 45 -> "f5", 46 -> "f6", 47 -> "f7", 48 -> "f8",
|
||||
49 -> "g1", 50 -> "g2", 51 -> "g3", 52 -> "g4", 53 -> "g5", 54 -> "g6",55 -> "g7", 56 -> "g8",
|
||||
57 -> "h1", 58 -> "h2", 59 -> "h3", 60 -> "h4", 61 -> "h5", 62 -> "h6", 63 -> "h7", 64 -> "h8"}
|
||||
1
Task/Knights-tour/Mathematica/knights-tour-4.math
Normal file
1
Task/Knights-tour/Mathematica/knights-tour-4.math
Normal file
|
|
@ -0,0 +1 @@
|
|||
knightsGraph = KnightTourGraph[i, i, VertexLabels -> vertexLabels, ImagePadding -> 15];
|
||||
1
Task/Knights-tour/Mathematica/knights-tour-5.math
Normal file
1
Task/Knights-tour/Mathematica/knights-tour-5.math
Normal file
|
|
@ -0,0 +1 @@
|
|||
hamiltonianCycle = ((FindHamiltonianCycle[knightsGraph] /. UndirectedEdge -> DirectedEdge) /. labels)[[1]];
|
||||
1
Task/Knights-tour/Mathematica/knights-tour-6.math
Normal file
1
Task/Knights-tour/Mathematica/knights-tour-6.math
Normal file
|
|
@ -0,0 +1 @@
|
|||
end = Cases[hamiltonianCycle, (x_ \[DirectedEdge] start) :> x][[1]];
|
||||
1
Task/Knights-tour/Mathematica/knights-tour-7.math
Normal file
1
Task/Knights-tour/Mathematica/knights-tour-7.math
Normal file
|
|
@ -0,0 +1 @@
|
|||
FindShortestPath[g, start, end]]
|
||||
61
Task/Knights-tour/Mathprog/knights-tour-1.math
Normal file
61
Task/Knights-tour/Mathprog/knights-tour-1.math
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*Knights.mathprog
|
||||
|
||||
Find a Knights Tour
|
||||
|
||||
Nigel_Galloway
|
||||
January 11th., 2012
|
||||
*/
|
||||
|
||||
param ZBLS;
|
||||
param ROWS;
|
||||
param COLS;
|
||||
param D := 2;
|
||||
set ROWSR := 1..ROWS;
|
||||
set COLSR := 1..COLS;
|
||||
set ROWSV := (1-D)..(ROWS+D);
|
||||
set COLSV := (1-D)..(COLS+D);
|
||||
param Iz{ROWSR,COLSR}, integer, default 0;
|
||||
set ZBLSV := 1..(ZBLS+1);
|
||||
set ZBLSR := 1..ZBLS;
|
||||
|
||||
var BR{ROWSV,COLSV,ZBLSV}, binary;
|
||||
|
||||
void0{r in ROWSV, z in ZBLSR,c in (1-D)..0}: BR[r,c,z] = 0;
|
||||
void1{r in ROWSV, z in ZBLSR,c in (COLS+1)..(COLS+D)}: BR[r,c,z] = 0;
|
||||
void2{c in COLSV, z in ZBLSR,r in (1-D)..0}: BR[r,c,z] = 0;
|
||||
void3{c in COLSV, z in ZBLSR,r in (ROWS+1)..(ROWS+D)}: BR[r,c,z] = 0;
|
||||
void4{r in ROWSV,c in (1-D)..0}: BR[r,c,ZBLS+1] = 1;
|
||||
void5{r in ROWSV,c in (COLS+1)..(COLS+D)}: BR[r,c,ZBLS+1] = 1;
|
||||
void6{c in COLSV,r in (1-D)..0}: BR[r,c,ZBLS+1] = 1;
|
||||
void7{c in COLSV,r in (ROWS+1)..(ROWS+D)}: BR[r,c,ZBLS+1] = 1;
|
||||
|
||||
Izfree{r in ROWSR, c in COLSR, z in ZBLSR : Iz[r,c] = -1}: BR[r,c,z] = 0;
|
||||
Iz1{Izr in ROWSR, Izc in COLSR, r in ROWSR, c in COLSR, z in ZBLSR : Izr=r and Izc=c and Iz[Izr,Izc]=z}: BR[r,c,z] = 1;
|
||||
|
||||
rule1{z in ZBLSR}: sum{r in ROWSR, c in COLSR} BR[r,c,z] = 1;
|
||||
rule2{r in ROWSR, c in COLSR}: sum{z in ZBLSV} BR[r,c,z] = 1;
|
||||
rule3{r in ROWSR, c in COLSR, z in ZBLSR}: BR[0,0,z+1] + BR[r-1,c-2,z+1] + BR[r-1,c+2,z+1] + BR[r-2,c-1,z+1] + BR[r-2,c+1,z+1] + BR[r+1,c+2,z+1] + BR[r+1,c-2,z+1] + BR[r+2,c-1,z+1] + BR[r+2,c+1,z+1] - BR[r,c,z] >= 0;
|
||||
|
||||
solve;
|
||||
|
||||
for {r in ROWSR} {
|
||||
for {c in COLSR} {
|
||||
printf " %2d", sum{z in ZBLSR} BR[r,c,z]*z;
|
||||
}
|
||||
printf "\n";
|
||||
}
|
||||
data;
|
||||
|
||||
param ROWS := 5;
|
||||
param COLS := 5;
|
||||
param ZBLS := 25;
|
||||
param
|
||||
Iz: 1 2 3 4 5 :=
|
||||
1 . . . . .
|
||||
2 . 19 2 . .
|
||||
3 . . . . .
|
||||
4 . . . . .
|
||||
5 . . . . .
|
||||
;
|
||||
|
||||
end;
|
||||
45
Task/Knights-tour/Mathprog/knights-tour-2.math
Normal file
45
Task/Knights-tour/Mathprog/knights-tour-2.math
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
GLPSOL: GLPK LP/MIP Solver, v4.47
|
||||
Parameter(s) specified in the command line:
|
||||
--minisat --math Knights.mathprog
|
||||
Reading model section from Knights.mathprog...
|
||||
Reading data section from Knights.mathprog...
|
||||
62 lines were read
|
||||
Generating void0...
|
||||
Generating void1...
|
||||
Generating void2...
|
||||
Generating void3...
|
||||
Generating void4...
|
||||
Generating void5...
|
||||
Generating void6...
|
||||
Generating void7...
|
||||
Generating Izfree...
|
||||
Generating Iz1...
|
||||
Generating rule1...
|
||||
Generating rule2...
|
||||
Generating rule3...
|
||||
Model has been successfully generated
|
||||
Will search for ANY feasible solution
|
||||
Translating to CNF-SAT...
|
||||
Original problem has 2549 rows, 2106 columns, and 9349 non-zeros
|
||||
575 covering inequalities
|
||||
1924 partitioning equalities
|
||||
Solving CNF-SAT problem...
|
||||
Instance has 3356 variables, 10874 clauses, and 34549 literals
|
||||
==================================[MINISAT]===================================
|
||||
| Conflicts | ORIGINAL | LEARNT | Progress |
|
||||
| | Clauses Literals | Limit Clauses Literals Lit/Cl | |
|
||||
==============================================================================
|
||||
| 0 | 9000 32675 | 3000 0 0 0.0 | 0.000 % |
|
||||
| 101 | 6025 21551 | 3300 93 1620 17.4 | 57.688 % |
|
||||
| 251 | 6025 21551 | 3630 243 4961 20.4 | 57.688 % |
|
||||
==============================================================================
|
||||
SATISFIABLE
|
||||
Objective value = 0.000000000e+000
|
||||
Time used: 0.0 secs
|
||||
Memory used: 6.5 Mb (6775701 bytes)
|
||||
1 12 7 18 3
|
||||
6 19 2 13 8
|
||||
11 22 15 4 17
|
||||
20 5 24 9 14
|
||||
23 10 21 16 25
|
||||
Model has been successfully processed
|
||||
64
Task/Knights-tour/Mathprog/knights-tour-3.math
Normal file
64
Task/Knights-tour/Mathprog/knights-tour-3.math
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*Knights.mathprog
|
||||
|
||||
Find a Knights Tour
|
||||
|
||||
Nigel_Galloway
|
||||
January 11th., 2012
|
||||
*/
|
||||
|
||||
param ZBLS;
|
||||
param ROWS;
|
||||
param COLS;
|
||||
param D := 2;
|
||||
set ROWSR := 1..ROWS;
|
||||
set COLSR := 1..COLS;
|
||||
set ROWSV := (1-D)..(ROWS+D);
|
||||
set COLSV := (1-D)..(COLS+D);
|
||||
param Iz{ROWSR,COLSR}, integer, default 0;
|
||||
set ZBLSV := 1..(ZBLS+1);
|
||||
set ZBLSR := 1..ZBLS;
|
||||
|
||||
var BR{ROWSV,COLSV,ZBLSV}, binary;
|
||||
|
||||
void0{r in ROWSV, z in ZBLSR,c in (1-D)..0}: BR[r,c,z] = 0;
|
||||
void1{r in ROWSV, z in ZBLSR,c in (COLS+1)..(COLS+D)}: BR[r,c,z] = 0;
|
||||
void2{c in COLSV, z in ZBLSR,r in (1-D)..0}: BR[r,c,z] = 0;
|
||||
void3{c in COLSV, z in ZBLSR,r in (ROWS+1)..(ROWS+D)}: BR[r,c,z] = 0;
|
||||
void4{r in ROWSV,c in (1-D)..0}: BR[r,c,ZBLS+1] = 1;
|
||||
void5{r in ROWSV,c in (COLS+1)..(COLS+D)}: BR[r,c,ZBLS+1] = 1;
|
||||
void6{c in COLSV,r in (1-D)..0}: BR[r,c,ZBLS+1] = 1;
|
||||
void7{c in COLSV,r in (ROWS+1)..(ROWS+D)}: BR[r,c,ZBLS+1] = 1;
|
||||
|
||||
Izfree{r in ROWSR, c in COLSR, z in ZBLSR : Iz[r,c] = -1}: BR[r,c,z] = 0;
|
||||
Iz1{Izr in ROWSR, Izc in COLSR, r in ROWSR, c in COLSR, z in ZBLSR : Izr=r and Izc=c and Iz[Izr,Izc]=z}: BR[r,c,z] = 1;
|
||||
|
||||
rule1{z in ZBLSR}: sum{r in ROWSR, c in COLSR} BR[r,c,z] = 1;
|
||||
rule2{r in ROWSR, c in COLSR}: sum{z in ZBLSV} BR[r,c,z] = 1;
|
||||
rule3{r in ROWSR, c in COLSR, z in ZBLSR}: BR[0,0,z+1] + BR[r-1,c-2,z+1] + BR[r-1,c+2,z+1] + BR[r-2,c-1,z+1] + BR[r-2,c+1,z+1] + BR[r+1,c+2,z+1] + BR[r+1,c-2,z+1] + BR[r+2,c-1,z+1] + BR[r+2,c+1,z+1] - BR[r,c,z] >= 0;
|
||||
|
||||
solve;
|
||||
|
||||
for {r in ROWSR} {
|
||||
for {c in COLSR} {
|
||||
printf " %2d", sum{z in ZBLSR} BR[r,c,z]*z;
|
||||
}
|
||||
printf "\n";
|
||||
}
|
||||
data;
|
||||
|
||||
param ROWS := 8;
|
||||
param COLS := 8;
|
||||
param ZBLS := 64;
|
||||
param
|
||||
Iz: 1 2 3 4 5 6 7 8 :=
|
||||
1 . . . . . . . .
|
||||
2 . . . . . . 48 .
|
||||
3 . . . . . . . .
|
||||
4 . . . . . . . .
|
||||
5 . . . . . . . .
|
||||
6 . . . . . . . .
|
||||
7 . 58 . . . . . .
|
||||
8 . . . . . . . .
|
||||
;
|
||||
|
||||
end;
|
||||
64
Task/Knights-tour/Mathprog/knights-tour-4.math
Normal file
64
Task/Knights-tour/Mathprog/knights-tour-4.math
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
GLPSOL: GLPK LP/MIP Solver, v4.47
|
||||
Parameter(s) specified in the command line:
|
||||
--minisat --math Knights.mathprog
|
||||
Reading model section from Knights.mathprog...
|
||||
Reading data section from Knights.mathprog...
|
||||
65 lines were read
|
||||
Generating void0...
|
||||
Generating void1...
|
||||
Generating void2...
|
||||
Generating void3...
|
||||
Generating void4...
|
||||
Generating void5...
|
||||
Generating void6...
|
||||
Generating void7...
|
||||
Generating Izfree...
|
||||
Generating Iz1...
|
||||
Generating rule1...
|
||||
Generating rule2...
|
||||
Generating rule3...
|
||||
Model has been successfully generated
|
||||
Will search for ANY feasible solution
|
||||
Translating to CNF-SAT...
|
||||
Original problem has 10466 rows, 9360 columns, and 55330 non-zeros
|
||||
3968 covering inequalities
|
||||
6370 partitioning equalities
|
||||
Solving CNF-SAT problem...
|
||||
Instance has 15056 variables, 46754 clauses, and 149794 literals
|
||||
==================================[MINISAT]===================================
|
||||
| Conflicts | ORIGINAL | LEARNT | Progress |
|
||||
| | Clauses Literals | Limit Clauses Literals Lit/Cl | |
|
||||
==============================================================================
|
||||
| 0 | 40512 143552 | 13504 0 0 0.0 | 0.000 % |
|
||||
| 100 | 32458 114610 | 14854 89 5138 57.7 | 46.633 % |
|
||||
| 250 | 32458 114610 | 16340 239 18544 77.6 | 46.633 % |
|
||||
| 475 | 27499 102956 | 17974 424 42212 99.6 | 46.892 % |
|
||||
| 813 | 27366 102490 | 19771 757 73184 96.7 | 51.541 % |
|
||||
| 1322 | 27366 102490 | 21748 1264 137991 109.2 | 52.245 % |
|
||||
| 2083 | 23226 92730 | 23923 2010 250286 124.5 | 53.620 % |
|
||||
| 3227 | 22239 90284 | 26315 3138 460582 146.8 | 53.620 % |
|
||||
| 4937 | 22239 90284 | 28947 4848 769486 158.7 | 53.620 % |
|
||||
| 7499 | 22206 90168 | 31842 7404 1258240 169.9 | 55.167 % |
|
||||
| 11346 | 21067 87284 | 35026 11248 2085553 185.4 | 55.167 % |
|
||||
| 17113 | 21067 87284 | 38528 17015 3625910 213.1 | 55.167 % |
|
||||
| 25763 | 21067 87284 | 42381 25665 5906283 230.1 | 55.167 % |
|
||||
| 38738 | 21051 87252 | 46619 38638 9316878 241.1 | 55.679 % |
|
||||
| 58199 | 21051 87252 | 51281 16434 3967196 241.4 | 55.685 % |
|
||||
| 87393 | 20707 86474 | 56410 45624 13013357 285.2 | 56.277 % |
|
||||
| 131184 | 20180 84834 | 62051 37252 8996727 241.5 | 56.542 % |
|
||||
| 196871 | 20180 84834 | 68256 49392 13807861 279.6 | 56.542 % |
|
||||
| 295399 | 20180 84834 | 75081 22688 5827696 256.9 | 56.542 % |
|
||||
==============================================================================
|
||||
SATISFIABLE
|
||||
Objective value = 0.000000000e+000
|
||||
Time used: 333.0 secs
|
||||
Memory used: 28.2 Mb (29609617 bytes)
|
||||
51 24 31 6 49 26 33 64
|
||||
30 5 50 25 32 63 48 43
|
||||
23 52 7 4 27 44 15 34
|
||||
8 29 60 45 62 47 42 17
|
||||
59 22 53 28 3 16 35 14
|
||||
54 9 56 61 46 39 18 41
|
||||
21 58 11 38 19 2 13 36
|
||||
10 55 20 57 12 37 40 1
|
||||
Model has been successfully processed
|
||||
102
Task/Knights-tour/Nim/knights-tour.nim
Normal file
102
Task/Knights-tour/Nim/knights-tour.nim
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import algorithm, options, random, parseutils, strutils, strformat
|
||||
|
||||
type
|
||||
Board[N: static Positive] = array[N, array[N, int]]
|
||||
Move = tuple[x, y: int]
|
||||
MoveList = array[8, Move]
|
||||
MoveIndexes = array[8, int]
|
||||
|
||||
const Moves: MoveList = [(2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1)]
|
||||
|
||||
proc `$`(board: Board): string =
|
||||
## Display the board.
|
||||
let size = len($(board.N * board.N)) + 1
|
||||
for row in board:
|
||||
for val in row:
|
||||
stdout.write ($val).align(size)
|
||||
echo ""
|
||||
|
||||
proc sortedMoves(board: Board; x, y: int): MoveIndexes =
|
||||
## Return the list of moves sorted by count of possible moves.
|
||||
|
||||
var counts: array[8, tuple[value, index: int]]
|
||||
for i, d1 in Moves:
|
||||
var count = 0
|
||||
for d2 in Moves:
|
||||
let x2 = x + d1.x + d2.x
|
||||
let y2 = y + d1.y + d2.y
|
||||
if x2 in 0..<board.N and y2 in 0..<board.N and board[y2][x2] == 0:
|
||||
inc count
|
||||
counts[i] = (count, i)
|
||||
|
||||
counts.shuffle() # Shuffle to randomly break ties.
|
||||
counts.sort() # Lexicographic sort.
|
||||
|
||||
for i, count in counts:
|
||||
result[i] = count.index
|
||||
|
||||
|
||||
proc knightTour[N: static Positive](start: string): Option[Board[N]] =
|
||||
## Return the knight tour for a board of size N x N and the starting
|
||||
## position "start.
|
||||
## If no solution is found, return "node" else return "some".
|
||||
|
||||
# Initialize the board with the starting position.
|
||||
var board: Board[N]
|
||||
var startx, starty: int
|
||||
startx = ord(start[0]) - ord('a')
|
||||
if startx notin 0..<N:
|
||||
raise newException(ValueError, "wrong column.")
|
||||
if parseInt(start, starty, 1) != start.len - 1 or starty notin 1..N:
|
||||
raise newException(ValueError, "wrong line.")
|
||||
starty = N - starty
|
||||
board[starty][startx] = 1
|
||||
|
||||
type OrderItem = tuple[x, y, idx: int; mi: MoveIndexes]
|
||||
var order: array[N * N, OrderItem]
|
||||
order[0] = (startx, starty, 0, board.sortedMoves(startx, starty))
|
||||
|
||||
# Search a tour.
|
||||
var n = 0
|
||||
while n < N * N - 1:
|
||||
let x = order[n].x
|
||||
let y = order[n].y
|
||||
var ok = false
|
||||
|
||||
for i in order[n].idx..7:
|
||||
let d = Moves[order[n].mi[i]]
|
||||
if x + d.x notin 0..<N or y + d.y notin 0..<N: continue
|
||||
if board[y + d.y][x + d.x] == 0:
|
||||
order[n].idx = i + 1
|
||||
inc n
|
||||
board[y + d.y][x + d.x] = n + 1
|
||||
order[n] = (x + d.x, y + d.y, 0, board.sortedMoves(x + d.x, y + d.y))
|
||||
ok = true
|
||||
break
|
||||
|
||||
if not ok:
|
||||
# Failed: backtrack.
|
||||
echo "backtrack"
|
||||
board[y][x] = 0
|
||||
dec n
|
||||
if n < 0: return none(Board[N]) # No solution found.
|
||||
|
||||
result = some(board)
|
||||
|
||||
|
||||
proc run[N: static Positive](start: string) =
|
||||
## Run the algorithm and display the result.
|
||||
let result = knightTour[N](start)
|
||||
echo &"Board size: {N}x{N}, starting position: {start}."
|
||||
if result.isSome(): echo result.get()
|
||||
else: echo "No solution found.\n"
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
||||
randomize()
|
||||
|
||||
run[5]("c3")
|
||||
#run[5]("c4") # No solution, so very slow compared to other cases.
|
||||
run[8]("b5")
|
||||
run[31]("a1")
|
||||
363
Task/Knights-tour/ObjectIcon/knights-tour.oi
Normal file
363
Task/Knights-tour/ObjectIcon/knights-tour.oi
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#
|
||||
# Find Knight’s Tours.
|
||||
#
|
||||
# Using Warnsdorff’s heuristic, find multiple solutions.
|
||||
#
|
||||
# Based on my ATS/Postiats program.
|
||||
#
|
||||
# The main difference from the ATS is this program uses a
|
||||
# co-expression pair to make a generator of solutions, whereas the ATS
|
||||
# simply prints solutions where they are found.
|
||||
#
|
||||
# Usage: ./knights_tour [START_POSITION [MAX_TOURS [closed]]]
|
||||
# Examples:
|
||||
# ./knights_tour (prints one tour starting from a1)
|
||||
# ./knights_tour c5
|
||||
# ./knights_tour c5 2000
|
||||
# ./knights_tour c5 2000 closed
|
||||
#
|
||||
|
||||
$define DEFAULT_NUMBER_OF_RANKS 8
|
||||
$define DEFAULT_NUMBER_OF_FILES 8
|
||||
|
||||
import io
|
||||
|
||||
procedure main(args)
|
||||
local f_out
|
||||
local tours
|
||||
local tour_board
|
||||
local n_tour
|
||||
local starting_position
|
||||
local i, j
|
||||
local max_tours
|
||||
local closed_only
|
||||
|
||||
starting_position := \algebraic_notation_to_i_j(args[1]) | [1, 1]
|
||||
i := starting_position[1]
|
||||
j := starting_position[2]
|
||||
|
||||
max_tours := integer(args[2]) | 1
|
||||
closed_only := if \args[3] === "closed" then &yes else &no
|
||||
|
||||
f_out := FileStream.stdout
|
||||
|
||||
tours := KnightsTours()
|
||||
n_tour := 0
|
||||
if n_tour < max_tours then
|
||||
every tour_board := tours.generate(i, j, closed_only) do
|
||||
{
|
||||
n_tour +:= 1
|
||||
write("Tour number ", n_tour)
|
||||
f_out.write(tour_board.make_moves_display())
|
||||
f_out.write(tour_board.make_board_display())
|
||||
f_out.write()
|
||||
if max_tours <= n_tour then
|
||||
break
|
||||
}
|
||||
end
|
||||
|
||||
procedure algebraic_notation_to_i_j(s)
|
||||
return [integer(s[2]), ord(s[1]) - ord('a') + 1]
|
||||
end
|
||||
|
||||
class Move()
|
||||
public const i
|
||||
public const j
|
||||
|
||||
public new(rank, file)
|
||||
i := rank
|
||||
j := file
|
||||
return
|
||||
end
|
||||
|
||||
public make_display(n_ranks)
|
||||
return char(ord('a') + j - 1) || i
|
||||
end
|
||||
end
|
||||
|
||||
class Chessboard()
|
||||
|
||||
public const n_ranks
|
||||
public const n_files
|
||||
public const n_squares
|
||||
private board
|
||||
|
||||
public new(num_ranks, num_files)
|
||||
/num_ranks := DEFAULT_NUMBER_OF_RANKS
|
||||
/num_files := DEFAULT_NUMBER_OF_FILES
|
||||
|
||||
n_files := num_files
|
||||
n_ranks := num_ranks
|
||||
n_squares := n_ranks * n_files
|
||||
board := list(n_squares)
|
||||
return
|
||||
end
|
||||
|
||||
public copy()
|
||||
local new_board
|
||||
local i
|
||||
|
||||
new_board := Chessboard(n_files, n_ranks)
|
||||
every i := 1 to n_squares do
|
||||
new_board.board[i] := board[i]
|
||||
return new_board
|
||||
end
|
||||
|
||||
public square(i, j)
|
||||
# The board is stored in column-major order.
|
||||
return board[i + (n_ranks * (j - 1))]
|
||||
end
|
||||
|
||||
public try(i, j, value)
|
||||
# Backtracking assignment. Though we use it for ordinary
|
||||
# assignment.
|
||||
#
|
||||
# The board is stored in column-major order.
|
||||
suspend board[i + (n_ranks * (j - 1))] <- value
|
||||
end
|
||||
|
||||
public make_board_display()
|
||||
local s
|
||||
local i, j
|
||||
|
||||
s := ""
|
||||
every i := n_ranks to 1 by -1 do
|
||||
{
|
||||
s ||:= " "
|
||||
every j := 1 to n_files do
|
||||
s ||:= "+----"
|
||||
s ||:= "+\n"
|
||||
s ||:= right(i, 2) || " "
|
||||
every j := 1 to n_files do
|
||||
s ||:= " | " || (\right(square(i, j), 2))
|
||||
s ||:= " |\n"
|
||||
}
|
||||
s ||:= " "
|
||||
every j := 1 to n_files do
|
||||
s ||:= "+----"
|
||||
s ||:= "+\n"
|
||||
s ||:= " "
|
||||
every j := 1 to n_files do
|
||||
s ||:= " " || char(ord('a') + j - 1)
|
||||
return s
|
||||
end
|
||||
|
||||
public make_moves_display()
|
||||
local positions
|
||||
local i, j
|
||||
local s
|
||||
local first_position, last_position
|
||||
|
||||
positions := list(n_squares)
|
||||
every i := 1 to n_ranks do
|
||||
every j := 1 to n_files do
|
||||
positions[square(i, j)] := Move(i, j)
|
||||
|
||||
s := ""
|
||||
every j := 1 to n_squares - 1 do
|
||||
{
|
||||
s ||:= positions[j].make_display()
|
||||
s ||:= (if j % n_files = 0 then " ->\n" else " -> ")
|
||||
}
|
||||
s ||:= positions[n_squares].make_display()
|
||||
|
||||
first_position := find_nth_position(1)
|
||||
last_position := find_nth_position(n_squares)
|
||||
if knight_positions_are_attacking(first_position.i,
|
||||
first_position.j,
|
||||
last_position.i,
|
||||
last_position.j) then
|
||||
s ||:= " -> cycle"
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
public find_nth_position(n)
|
||||
local i, j
|
||||
local position
|
||||
|
||||
position := &null
|
||||
i := 1
|
||||
while /position & i <= n_ranks do
|
||||
{
|
||||
j := 1
|
||||
while /position & j <= n_files do
|
||||
{
|
||||
if square(i, j) = n then
|
||||
position := Move(i, j)
|
||||
j +:= 1
|
||||
}
|
||||
i +:= 1
|
||||
}
|
||||
return position
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class KnightsTours()
|
||||
|
||||
public const n_ranks
|
||||
public const n_files
|
||||
public const n_squares
|
||||
private board
|
||||
|
||||
public new(num_ranks, num_files, i, j, closed_only)
|
||||
board := Chessboard(num_ranks, num_files)
|
||||
n_ranks := board.n_ranks
|
||||
n_files := board.n_files
|
||||
n_squares := board.n_squares
|
||||
return
|
||||
end
|
||||
|
||||
public generate(i, j, closed_only)
|
||||
# i,j = starting position.
|
||||
|
||||
local consumer
|
||||
local explorer
|
||||
local tour_board
|
||||
|
||||
# Simple coroutines. The consumer receives complete tours (each in
|
||||
# the form of a Chessboard) from the explorer.
|
||||
consumer := ¤t
|
||||
explorer := create explore(consumer, i, j, 1,
|
||||
closed_only, i, j)
|
||||
|
||||
while tour_board := @explorer do
|
||||
suspend tour_board
|
||||
end
|
||||
|
||||
private explore(consumer, i, j, n_position,
|
||||
closed_only, i_start, j_start)
|
||||
# i,j = starting position.
|
||||
board.try(i, j, n_position)
|
||||
explore_inner(consumer, i, j, n_position,
|
||||
closed_only, i_start, j_start)
|
||||
board.try(i, j, &null)
|
||||
end
|
||||
|
||||
private explore_inner(consumer, i, j, n_position,
|
||||
closed_only, i_start, j_start)
|
||||
local moves, mv
|
||||
|
||||
if n_squares - n_position = 1 then
|
||||
{
|
||||
# Is the last move possible? If so, make it and output the
|
||||
# board. (Only zero or one of the moves can be non-null.)
|
||||
moves := possible_moves(i, j)
|
||||
every try_last_move(consumer, moves[1 to 8],
|
||||
closed_only, i_start, j_start)
|
||||
}
|
||||
else
|
||||
{
|
||||
moves := next_moves(i, j, n_position)
|
||||
every mv := !moves do
|
||||
if \mv then
|
||||
explore(consumer, mv.i, mv.j, n_position + 1,
|
||||
closed_only, i_start, j_start)
|
||||
}
|
||||
end
|
||||
|
||||
private try_last_move(consumer, move, closed_only, i_start, j_start)
|
||||
if \move then
|
||||
if (/closed_only |
|
||||
knight_positions_are_attacking(move.i, move.j,
|
||||
i_start, j_start)) then
|
||||
{
|
||||
board.try(move.i, move.j, n_squares)
|
||||
(board.copy())@consumer
|
||||
board.try(move.i, move.j, &null)
|
||||
}
|
||||
end
|
||||
|
||||
private next_moves(i, j, n_position)
|
||||
local moves
|
||||
local w_list, w
|
||||
local k
|
||||
|
||||
moves := possible_moves(i, j)
|
||||
w_list := list(8)
|
||||
every k := 1 to 8 do
|
||||
w_list[k] := count_following_moves(moves[k], n_position)
|
||||
w := pick_w(w_list)
|
||||
if w = 0 then
|
||||
# A dead end.
|
||||
moves := list(8, &null)
|
||||
else
|
||||
# w is least positive number of following moves. Nullify any
|
||||
# move that has either zero following moves (it is a dead end)
|
||||
# or more than w following moves (it violates Warnsdorff’s
|
||||
# heuristic).
|
||||
every k := 1 to 8 do
|
||||
if w_list[k] ~= w then
|
||||
moves[k] := &null
|
||||
return moves
|
||||
end
|
||||
|
||||
private count_following_moves(move, n_position)
|
||||
local w
|
||||
local following_moves
|
||||
|
||||
w := 0
|
||||
if \move then
|
||||
{
|
||||
board.try(move.i, move.j, n_position + 1)
|
||||
following_moves := possible_moves(move.i, move.j)
|
||||
every ( \following_moves[1 to 8] & w +:= 1 )
|
||||
board.try(move.i, move.j, &null)
|
||||
}
|
||||
return w
|
||||
end
|
||||
|
||||
private pick_w(w_list)
|
||||
local w
|
||||
|
||||
w := 0
|
||||
every w := next_pick (w, w_list[1 to 8])
|
||||
return w
|
||||
end
|
||||
|
||||
private next_pick(u, v)
|
||||
local w
|
||||
|
||||
if v = 0 then
|
||||
w := u
|
||||
else if u = 0 then
|
||||
w := v
|
||||
else
|
||||
w := min (u, v)
|
||||
return w
|
||||
end
|
||||
|
||||
private possible_moves(i, j)
|
||||
local move1, move2, move3, move4
|
||||
local move5, move6, move7, move8
|
||||
|
||||
move1 := try_move(i + 1, j + 2)
|
||||
move2 := try_move(i + 2, j + 1)
|
||||
move3 := try_move(i + 1, j - 2)
|
||||
move4 := try_move(i + 2, j - 1)
|
||||
move5 := try_move(i - 1, j + 2)
|
||||
move6 := try_move(i - 2, j + 1)
|
||||
move7 := try_move(i - 1, j - 2)
|
||||
move8 := try_move(i - 2, j - 1)
|
||||
return [move1, move2, move3, move4,
|
||||
move5, move6, move7, move8]
|
||||
end
|
||||
|
||||
private try_move(i1, j1)
|
||||
return (1 <= i1 <= n_ranks &
|
||||
1 <= j1 <= n_files &
|
||||
/board.square(i1, j1) &
|
||||
Move(i1, j1)) | &null
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
procedure knight_positions_are_attacking(i1, j1, i2, j2)
|
||||
local i_diff, j_diff
|
||||
|
||||
i_diff := abs(i1 - i2)
|
||||
j_diff := abs(j1 - j2)
|
||||
return (((i_diff = 2 & j_diff = 1) |
|
||||
(i_diff = 1 & j_diff = 2)) & &yes) | fail
|
||||
end
|
||||
87
Task/Knights-tour/Perl/knights-tour.pl
Normal file
87
Task/Knights-tour/Perl/knights-tour.pl
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
# Find a knight's tour
|
||||
|
||||
my @board;
|
||||
|
||||
# Choose starting position - may be passed in on command line; if
|
||||
# not, choose random square.
|
||||
my ($i, $j);
|
||||
if (my $sq = shift @ARGV) {
|
||||
die "$0: illegal start square '$sq'\n" unless ($i, $j) = from_algebraic($sq);
|
||||
} else {
|
||||
($i, $j) = (int rand 8, int rand 8);
|
||||
}
|
||||
|
||||
# Move sequence
|
||||
my @moves = ();
|
||||
|
||||
foreach my $move (1..64) {
|
||||
# Record current move
|
||||
push @moves, to_algebraic($i,$j);
|
||||
$board[$i][$j] = $move;
|
||||
|
||||
# Get list of possible next moves
|
||||
my @targets = possible_moves($i,$j);
|
||||
|
||||
# Find the one with the smallest degree
|
||||
my @min = (9);
|
||||
foreach my $target (@targets) {
|
||||
my ($ni, $nj) = @$target;
|
||||
my $next = possible_moves($ni,$nj);
|
||||
@min = ($next, $ni, $nj) if $next < $min[0];
|
||||
}
|
||||
|
||||
# And make it
|
||||
($i, $j) = @min[1,2];
|
||||
}
|
||||
|
||||
# Print the move list
|
||||
for (my $i=0; $i<4; ++$i) {
|
||||
for (my $j=0; $j<16; ++$j) {
|
||||
my $n = $i*16+$j;
|
||||
print $moves[$n];
|
||||
print ', ' unless $n+1 >= @moves;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
|
||||
# And the board, with move numbers
|
||||
for (my $i=0; $i<8; ++$i) {
|
||||
for (my $j=0; $j<8; ++$j) {
|
||||
# Assumes (1) ANSI sequences work, and (2) output
|
||||
# is light text on a dark background.
|
||||
print "\e[7m" if ($i%2==$j%2);
|
||||
printf " %2d", $board[$i][$j];
|
||||
print "\e[0m";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Find the list of positions the knight can move to from the given square
|
||||
sub possible_moves
|
||||
{
|
||||
my ($i, $j) = @_;
|
||||
return grep { $_->[0] >= 0 && $_->[0] < 8
|
||||
&& $_->[1] >= 0 && $_->[1] < 8
|
||||
&& !$board[$_->[0]][$_->[1]] } (
|
||||
[$i-2,$j-1], [$i-2,$j+1], [$i-1,$j-2], [$i-1,$j+2],
|
||||
[$i+1,$j-2], [$i+1,$j+2], [$i+2,$j-1], [$i+2,$j+1]);
|
||||
}
|
||||
|
||||
# Return the algebraic name of the square identified by the coordinates
|
||||
# i=rank, 0=black's home row; j=file, 0=white's queen's rook
|
||||
sub to_algebraic
|
||||
{
|
||||
my ($i, $j) = @_;
|
||||
chr(ord('a') + $j) . (8-$i);
|
||||
}
|
||||
|
||||
# Return the coordinates matching the given algebraic name
|
||||
sub from_algebraic
|
||||
{
|
||||
my $square = shift;
|
||||
return unless $square =~ /^([a-h])([1-8])$/;
|
||||
return (8-$2, ord($1) - ord('a'));
|
||||
}
|
||||
86
Task/Knights-tour/Phix/knights-tour.phix
Normal file
86
Task/Knights-tour/Phix/knights-tour.phix
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">size</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">nchars</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">" %d"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">size</span><span style="color: #0000FF;">*</span><span style="color: #000000;">size</span><span style="color: #0000FF;">)),</span>
|
||||
<span style="color: #000000;">fmt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">" %%%dd"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span>
|
||||
<span style="color: #000000;">blank</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">)</span>
|
||||
|
||||
<span style="color: #000080;font-style:italic;">-- to simplify output, each square is nchars</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">board</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">size</span><span style="color: #0000FF;">*</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">),</span><span style="color: #000000;">size</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000080;font-style:italic;">-- keep current counts, immediately backtrack if any hit 0
|
||||
-- (in line with the above, we only use every nth entry)</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">warnsdorffs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">size</span><span style="color: #0000FF;">*</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">),</span><span style="color: #000000;">size</span><span style="color: #0000FF;">)</span>
|
||||
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">ROW</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">COL</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">moves</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}}</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">onboard</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">row</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">size</span> <span style="color: #008080;">and</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">nchars</span> <span style="color: #008080;">and</span> <span style="color: #000000;">col</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">*</span><span style="color: #000000;">size</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">init_warnsdorffs</span><span style="color: #0000FF;">()</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">size</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">=</span><span style="color: #000000;">nchars</span> <span style="color: #008080;">to</span> <span style="color: #000000;">nchars</span><span style="color: #0000FF;">*</span><span style="color: #000000;">size</span> <span style="color: #008080;">by</span> <span style="color: #000000;">nchars</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">move</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">nrow</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">+</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ROW</span><span style="color: #0000FF;">],</span>
|
||||
<span style="color: #000000;">ncol</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">+</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COL</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">nchars</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">onboard</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">warnsdorffs</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
|
||||
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">t0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">(),</span>
|
||||
<span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">tries</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()></span><span style="color: #000000;">t1</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #0000FF;">?{</span><span style="color: #000000;">row</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">col</span><span style="color: #0000FF;">/</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">),</span><span style="color: #000000;">n</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tries</span><span style="color: #0000FF;">}</span>
|
||||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">board</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">))</span>
|
||||
<span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">tries</span><span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">></span><span style="color: #000000;">size</span><span style="color: #0000FF;">*</span><span style="color: #000000;">size</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">wmoves</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ncol</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">move</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">nrow</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">+</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ROW</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000000;">ncol</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">+</span><span style="color: #000000;">moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">move</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COL</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">nchars</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">onboard</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">and</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">wmoves</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">warnsdorffs</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">],</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">})</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #000000;">wmoves</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sort</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000080;font-style:italic;">-- avoid creating orphans</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">)<</span><span style="color: #000000;">2</span> <span style="color: #008080;">or</span> <span style="color: #000000;">wmoves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]></span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">m</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #0000FF;">{?,</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">wmoves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">m</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000000;">warnsdorffs</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">m</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #0000FF;">{?,</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">wmoves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">m</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">scol</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ncol</span><span style="color: #0000FF;">-</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span>
|
||||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">scol</span><span style="color: #0000FF;">..</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fmt</span><span style="color: #0000FF;">,</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">,</span><span style="color: #000000;">n</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">scol</span><span style="color: #0000FF;">..</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">blank</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">m</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wmoves</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #0000FF;">{?,</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">wmoves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">m</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000000;">warnsdorffs</span><span style="color: #0000FF;">[</span><span style="color: #000000;">nrow</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ncol</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #000000;">init_warnsdorffs</span><span style="color: #0000FF;">()</span>
|
||||
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'1'</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nchars</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">board</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">))</span>
|
||||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\nsolution found in %d tries (%3.2fs)\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tries</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">time</span><span style="color: #0000FF;">()-</span><span style="color: #000000;">t0</span><span style="color: #0000FF;">})</span>
|
||||
<span style="color: #008080;">else</span>
|
||||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"no solutions found\n"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<!--
|
||||
35
Task/Knights-tour/Picat/knights-tour.picat
Normal file
35
Task/Knights-tour/Picat/knights-tour.picat
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import cp.
|
||||
|
||||
main =>
|
||||
N = 8,
|
||||
A = new_array(N,N),
|
||||
foreach (R in 1..N, C in 1..N)
|
||||
Connected = [(R+1, C+2),
|
||||
(R+1, C-2),
|
||||
(R-1, C+2),
|
||||
(R-1, C-2),
|
||||
(R+2, C+1),
|
||||
(R+2, C-1),
|
||||
(R-2, C+1),
|
||||
(R-2, C-1)],
|
||||
A[R,C] :: [(R1-1)*N+C1 : (R1,C1) in Connected, R1 >= 1, R1 =< N, C1 >= 1, C1 =< N]
|
||||
end,
|
||||
V = vars(A),
|
||||
circuit(V),
|
||||
solve([ff],V),
|
||||
OutputM = new_array(N,N),
|
||||
fill_output_matrix(N,OutputM,V,1,1),
|
||||
foreach (R in 1..N)
|
||||
foreach (C in 1..N)
|
||||
printf("%3d ", OutputM[R,C])
|
||||
end,
|
||||
nl
|
||||
end.
|
||||
|
||||
fill_output_matrix(N,OutputM,V,I,Count) =>
|
||||
if Count =< N*N then
|
||||
R = (I-1) div N + 1,
|
||||
C = (I-1) mod N + 1,
|
||||
OutputM[R,C] = Count,
|
||||
fill_output_matrix(N,OutputM,V,V[I],Count+1)
|
||||
end.
|
||||
30
Task/Knights-tour/PicoLisp/knights-tour.l
Normal file
30
Task/Knights-tour/PicoLisp/knights-tour.l
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
(load "@lib/simul.l")
|
||||
|
||||
# Build board
|
||||
(grid 8 8)
|
||||
|
||||
# Generate legal moves for a given position
|
||||
(de moves (Tour)
|
||||
(extract
|
||||
'((Jump)
|
||||
(let? Pos (Jump (car Tour))
|
||||
(unless (memq Pos Tour)
|
||||
Pos ) ) )
|
||||
(quote # (taken from "games/chess.l")
|
||||
((This) (: 0 1 1 0 -1 1 0 -1 1)) # South Southwest
|
||||
((This) (: 0 1 1 0 -1 1 0 1 1)) # West Southwest
|
||||
((This) (: 0 1 1 0 -1 -1 0 1 1)) # West Northwest
|
||||
((This) (: 0 1 1 0 -1 -1 0 -1 -1)) # North Northwest
|
||||
((This) (: 0 1 -1 0 -1 -1 0 -1 -1)) # North Northeast
|
||||
((This) (: 0 1 -1 0 -1 -1 0 1 -1)) # East Northeast
|
||||
((This) (: 0 1 -1 0 -1 1 0 1 -1)) # East Southeast
|
||||
((This) (: 0 1 -1 0 -1 1 0 -1 1)) ) ) ) # South Southeast
|
||||
|
||||
# Build a list of moves, using Warnsdorff’s algorithm
|
||||
(let Tour '(b1) # Start at b1
|
||||
(while
|
||||
(mini
|
||||
'((P) (length (moves (cons P Tour))))
|
||||
(moves Tour) )
|
||||
(push 'Tour @) )
|
||||
(flip Tour) )
|
||||
110
Task/Knights-tour/PostScript/knights-tour.ps
Normal file
110
Task/Knights-tour/PostScript/knights-tour.ps
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
%!PS-Adobe-3.0
|
||||
%%BoundingBox: 0 0 300 300
|
||||
|
||||
/s { 300 n div } def
|
||||
/l { rlineto } def
|
||||
|
||||
% draws a square
|
||||
/bx { s mul exch s mul moveto s 0 l 0 s l s neg 0 l 0 s neg l } def
|
||||
|
||||
% draws checker board
|
||||
/xbd { 1 setgray
|
||||
0 0 moveto 300 0 l 0 300 l -300 0 l fill
|
||||
.7 1 .6 setrgbcolor
|
||||
0 1 n1 { dup 2 mod 2 n1 { 1 index bx fill } for pop } for
|
||||
0 setgray
|
||||
} def
|
||||
|
||||
/ar1 { [ exch { 0 } repeat ] } def
|
||||
/ar2 { [ exch dup { dup ar1 exch } repeat pop ] } def
|
||||
|
||||
/neighbors {
|
||||
-1 2 0
|
||||
1 2 0
|
||||
2 1 0
|
||||
2 -1 0
|
||||
1 -2 0
|
||||
-1 -2 0
|
||||
-2 -1 0
|
||||
-2 1 0
|
||||
%24 x y add 3 mul roll
|
||||
} def
|
||||
|
||||
/func { 0 dict begin mark } def
|
||||
/var { counttomark -1 1 { 2 add -1 roll def } for cleartomark } def
|
||||
|
||||
% x y can_goto -> bool
|
||||
/can_goto {
|
||||
func /x /y var
|
||||
x 0 ge
|
||||
x n lt
|
||||
y 0 ge
|
||||
y n lt
|
||||
and and and {
|
||||
occupied x get y get 0 eq
|
||||
} { false } ifelse
|
||||
end
|
||||
} def
|
||||
|
||||
% x y num_access -> number of cells reachable from (x,y)
|
||||
/num_access {
|
||||
func /x /y var
|
||||
/count 0 def
|
||||
x y can_goto {
|
||||
neighbors
|
||||
8 { pop y add exch x add exch can_goto {
|
||||
/count count 1 add def
|
||||
} if
|
||||
} repeat
|
||||
count 0 gt { count } { 9 } ifelse
|
||||
} { 10 } ifelse
|
||||
end
|
||||
} def
|
||||
|
||||
% a circle
|
||||
/marker { x s mul y s mul s 20 div 0 360 arc fill } def
|
||||
|
||||
% n solve -> draws board of size n x n, calcs path and draws it
|
||||
/solve {
|
||||
func /n var
|
||||
/n1 n 1 sub def
|
||||
|
||||
/c false def
|
||||
|
||||
8 n div setlinewidth
|
||||
gsave
|
||||
|
||||
0 1 n1 { /x exch def c not {
|
||||
0 1 n1 {
|
||||
/occupied n ar2 def
|
||||
c not {
|
||||
/c true def
|
||||
/y exch def
|
||||
grestore xbd gsave
|
||||
s 2 div dup translate
|
||||
n n mul 2 sub -1 0 { /iter exch def
|
||||
c {
|
||||
0 setgray marker x s mul y s mul moveto
|
||||
occupied x get y 1 put
|
||||
neighbors
|
||||
8 { pop y add exch x add exch 2 copy num_access 24 3 roll } repeat
|
||||
7 { dup 4 index lt { 6 3 roll } if pop pop pop } repeat
|
||||
|
||||
9 ge iter 0 gt and { /c false def } if
|
||||
/y exch def
|
||||
/x exch def
|
||||
.2 setgray x s mul y s mul lineto stroke
|
||||
} if } for
|
||||
% to be nice, draw box at final position
|
||||
.5 0 0 setrgbcolor marker
|
||||
y .5 sub x .5 sub bx 1 setlinewidth stroke
|
||||
stroke
|
||||
} if
|
||||
} for } if } for showpage
|
||||
grestore
|
||||
end
|
||||
} def
|
||||
|
||||
3 1 100 { solve } for
|
||||
|
||||
%%EOF
|
||||
80
Task/Knights-tour/Prolog/knights-tour-1.pro
Normal file
80
Task/Knights-tour/Prolog/knights-tour-1.pro
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
% N is the number of lines of the chessboard
|
||||
knight(N) :-
|
||||
Max is N * N,
|
||||
length(L, Max),
|
||||
knight(N, 0, Max, 0, 0, L),
|
||||
display(N, 0, L).
|
||||
|
||||
% knight(NbCol, Coup, Max, Lig, Col, L),
|
||||
% NbCol : number of columns per line
|
||||
% Coup : number of the current move
|
||||
% Max : maximum number of moves
|
||||
% Lig/ Col : current position of the knight
|
||||
% L : the "chessboard"
|
||||
|
||||
% the game is over
|
||||
knight(_, Max, Max, _, _, _) :- !.
|
||||
|
||||
knight(NbCol, N, MaxN, Lg, Cl, L) :-
|
||||
% Is the move legal
|
||||
Lg >= 0, Cl >= 0, Lg < NbCol, Cl < NbCol,
|
||||
|
||||
Pos is Lg * NbCol + Cl,
|
||||
N1 is N+1,
|
||||
% is the place free
|
||||
nth0(Pos, L, N1),
|
||||
|
||||
LgM1 is Lg - 1, LgM2 is Lg - 2, LgP1 is Lg + 1, LgP2 is Lg + 2,
|
||||
ClM1 is Cl - 1, ClM2 is Cl - 2, ClP1 is Cl + 1, ClP2 is Cl + 2,
|
||||
maplist(best_move(NbCol, L),
|
||||
[(LgP1, ClM2), (LgP2, ClM1), (LgP2, ClP1),(LgP1, ClP2),
|
||||
(LgM1, ClM2), (LgM2, ClM1), (LgM2, ClP1),(LgM1, ClP2)],
|
||||
R),
|
||||
sort(R, RS),
|
||||
pairs_values(RS, Moves),
|
||||
|
||||
move(NbCol, N1, MaxN, Moves, L).
|
||||
|
||||
move(NbCol, N1, MaxN, [(Lg, Cl) | R], L) :-
|
||||
knight(NbCol, N1, MaxN, Lg, Cl, L);
|
||||
move(NbCol, N1, MaxN, R, L).
|
||||
|
||||
%% An illegal move is scored 1000
|
||||
best_move(NbCol, _L, (Lg, Cl), 1000-(Lg, Cl)) :-
|
||||
( Lg < 0 ; Cl < 0; Lg >= NbCol; Cl >= NbCol), !.
|
||||
|
||||
best_move(NbCol, L, (Lg, Cl), 1000-(Lg, Cl)) :-
|
||||
Pos is Lg*NbCol+Cl,
|
||||
nth0(Pos, L, V),
|
||||
\+var(V), !.
|
||||
|
||||
%% a legal move is scored with the number of moves a knight can make
|
||||
best_move(NbCol, L, (Lg, Cl), R-(Lg, Cl)) :-
|
||||
LgM1 is Lg - 1, LgM2 is Lg - 2, LgP1 is Lg + 1, LgP2 is Lg + 2,
|
||||
ClM1 is Cl - 1, ClM2 is Cl - 2, ClP1 is Cl + 1, ClP2 is Cl + 2,
|
||||
include(possible_move(NbCol, L),
|
||||
[(LgP1, ClM2), (LgP2, ClM1), (LgP2, ClP1),(LgP1, ClP2),
|
||||
(LgM1, ClM2), (LgM2, ClM1), (LgM2, ClP1),(LgM1, ClP2)],
|
||||
Res),
|
||||
length(Res, Len),
|
||||
( Len = 0 -> R = 1000; R = Len).
|
||||
|
||||
% test if a place is enabled
|
||||
possible_move(NbCol, L, (Lg, Cl)) :-
|
||||
% move must be legal
|
||||
Lg >= 0, Cl >= 0, Lg < NbCol, Cl < NbCol,
|
||||
Pos is Lg * NbCol + Cl,
|
||||
% place must be free
|
||||
nth0(Pos, L, V),
|
||||
var(V).
|
||||
|
||||
|
||||
display(_, _, []).
|
||||
display(N, N, L) :-
|
||||
nl,
|
||||
display(N, 0, L).
|
||||
|
||||
display(N, M, [H | T]) :-
|
||||
writef('%3r', [H]),
|
||||
M1 is M + 1,
|
||||
display(N, M1, T).
|
||||
57
Task/Knights-tour/Prolog/knights-tour-2.pro
Normal file
57
Task/Knights-tour/Prolog/knights-tour-2.pro
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
:- initialization(main).
|
||||
|
||||
|
||||
board_size(8).
|
||||
in_board(X*Y) :- board_size(N), between(1,N,Y), between(1,N,X).
|
||||
|
||||
|
||||
% express jump-graph in dynamic "move"-rules
|
||||
make_graph :-
|
||||
findall(_, (in_board(P), assert_moves(P)), _).
|
||||
|
||||
% where
|
||||
assert_moves(P) :-
|
||||
findall(_, (can_move(P,Q), asserta(move(P,Q))), _).
|
||||
|
||||
can_move(X*Y,Q) :-
|
||||
( one(X,X1), two(Y,Y1) ; two(X,X1), one(Y,Y1) )
|
||||
, Q = X1*Y1, in_board(Q)
|
||||
. % where
|
||||
one(M,N) :- succ(M,N) ; succ(N,M).
|
||||
two(M,N) :- N is M + 2 ; N is M - 2.
|
||||
|
||||
|
||||
|
||||
hamiltonian(P,Pn) :-
|
||||
board_size(N), Size is N * N
|
||||
, hamiltonian(P,Size,[],Ps), enumerate(Size,Ps,Pn)
|
||||
.
|
||||
% where
|
||||
enumerate(_, [] , [] ).
|
||||
enumerate(N, [P|Ps], [N:P|Pn]) :- succ(M,N), enumerate(M,Ps,Pn).
|
||||
|
||||
|
||||
hamiltonian(P,N,Ps,Res) :-
|
||||
N =:= 1 -> Res = [P|Ps]
|
||||
; warnsdorff(Ps,P,Q), succ(M,N)
|
||||
, hamiltonian(Q,M,[P|Ps],Res)
|
||||
.
|
||||
% where
|
||||
warnsdorff(Ps,P,Q) :-
|
||||
moves(Ps,P,Qs), maplist(next_moves(Ps), Qs, Xs)
|
||||
, keysort(Xs,Ys), member(_-Q,Ys)
|
||||
.
|
||||
next_moves(Ps,Q,L-Q) :- moves(Ps,Q,Rs), length(Rs,L).
|
||||
|
||||
moves(Ps,P,Qs) :-
|
||||
findall(Q, (move(P,Q), \+ member(Q,Ps)), Qs).
|
||||
|
||||
|
||||
|
||||
show_path(Pn) :- findall(_, (in_board(P), show_cell(Pn,P)), _).
|
||||
% where
|
||||
show_cell(Pn,X*Y) :-
|
||||
member(N:X*Y,Pn), format('%3.0d',[N]), board_size(X), nl.
|
||||
|
||||
|
||||
main :- make_graph, hamiltonian(5*3,Pn), show_path(Pn), halt.
|
||||
65
Task/Knights-tour/Python/knights-tour.py
Normal file
65
Task/Knights-tour/Python/knights-tour.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import copy
|
||||
|
||||
boardsize=6
|
||||
_kmoves = ((2,1), (1,2), (-1,2), (-2,1), (-2,-1), (-1,-2), (1,-2), (2,-1))
|
||||
|
||||
|
||||
def chess2index(chess, boardsize=boardsize):
|
||||
'Convert Algebraic chess notation to internal index format'
|
||||
chess = chess.strip().lower()
|
||||
x = ord(chess[0]) - ord('a')
|
||||
y = boardsize - int(chess[1:])
|
||||
return (x, y)
|
||||
|
||||
def boardstring(board, boardsize=boardsize):
|
||||
r = range(boardsize)
|
||||
lines = ''
|
||||
for y in r:
|
||||
lines += '\n' + ','.join('%2i' % board[(x,y)] if board[(x,y)] else ' '
|
||||
for x in r)
|
||||
return lines
|
||||
|
||||
def knightmoves(board, P, boardsize=boardsize):
|
||||
Px, Py = P
|
||||
kmoves = set((Px+x, Py+y) for x,y in _kmoves)
|
||||
kmoves = set( (x,y)
|
||||
for x,y in kmoves
|
||||
if 0 <= x < boardsize
|
||||
and 0 <= y < boardsize
|
||||
and not board[(x,y)] )
|
||||
return kmoves
|
||||
|
||||
def accessibility(board, P, boardsize=boardsize):
|
||||
access = []
|
||||
brd = copy.deepcopy(board)
|
||||
for pos in knightmoves(board, P, boardsize=boardsize):
|
||||
brd[pos] = -1
|
||||
access.append( (len(knightmoves(brd, pos, boardsize=boardsize)), pos) )
|
||||
brd[pos] = 0
|
||||
return access
|
||||
|
||||
def knights_tour(start, boardsize=boardsize, _debug=False):
|
||||
board = {(x,y):0 for x in range(boardsize) for y in range(boardsize)}
|
||||
move = 1
|
||||
P = chess2index(start, boardsize)
|
||||
board[P] = move
|
||||
move += 1
|
||||
if _debug:
|
||||
print(boardstring(board, boardsize=boardsize))
|
||||
while move <= len(board):
|
||||
P = min(accessibility(board, P, boardsize))[1]
|
||||
board[P] = move
|
||||
move += 1
|
||||
if _debug:
|
||||
print(boardstring(board, boardsize=boardsize))
|
||||
input('\n%2i next: ' % move)
|
||||
return board
|
||||
|
||||
if __name__ == '__main__':
|
||||
while 1:
|
||||
boardsize = int(input('\nboardsize: '))
|
||||
if boardsize < 5:
|
||||
continue
|
||||
start = input('Start position: ')
|
||||
board = knights_tour(start, boardsize)
|
||||
print(boardstring(board, boardsize=boardsize))
|
||||
69
Task/Knights-tour/R/knights-tour.r
Normal file
69
Task/Knights-tour/R/knights-tour.r
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/Rscript
|
||||
|
||||
# M x N Chess Board.
|
||||
M = 8; N = 8; board = matrix(0, nrow = M, ncol = N)
|
||||
|
||||
# Get/Set value on a board position.
|
||||
getboard = function (position) { board[position[1], position[2]] }
|
||||
setboard = function (position, x) { board[position[1], position[2]] <<- x }
|
||||
|
||||
# (Relative) Hops of a Knight.
|
||||
hops = cbind(c(-2, -1), c(-1, -2), c(+1, -2), c(+2, -1),
|
||||
c(+2, +1), c(+1, +2), c(-1, +2), c(-2, +1))
|
||||
|
||||
# Validate a move.
|
||||
valid = function (move) {
|
||||
all(1 <= move & move <= c(M, N)) && (getboard(move) == 0)
|
||||
}
|
||||
|
||||
# Moves possible from a given position.
|
||||
explore = function (position) {
|
||||
moves = position + hops
|
||||
cbind(moves[, apply(moves, 2, valid)])
|
||||
}
|
||||
|
||||
# Possible moves sorted according to their Wornsdorff cost.
|
||||
candidates = function (position) {
|
||||
moves = explore(position)
|
||||
|
||||
# No candidate moves available.
|
||||
if (ncol(moves) == 0) { return(moves) }
|
||||
|
||||
wcosts = apply(moves, 2, function (position) { ncol(explore(position)) })
|
||||
cbind(moves[, order(wcosts)])
|
||||
}
|
||||
|
||||
# Recursive function for touring the chess board.
|
||||
knightTour = function (position, moveN) {
|
||||
|
||||
# Tour Complete.
|
||||
if (moveN > (M * N)) {
|
||||
print(board)
|
||||
quit()
|
||||
}
|
||||
|
||||
# Available moves.
|
||||
moves = candidates(position)
|
||||
|
||||
# None possible. Backtrack.
|
||||
if (ncol(moves) == 0) { return() }
|
||||
|
||||
# Make a move, and continue the tour.
|
||||
apply(moves, 2, function (position) {
|
||||
setboard(position, moveN)
|
||||
knightTour(position, moveN + 1)
|
||||
setboard(position, 0)
|
||||
})
|
||||
}
|
||||
|
||||
# User Input: Starting position (in algebraic notation).
|
||||
square = commandArgs(trailingOnly = TRUE)
|
||||
|
||||
# Convert into board co-ordinates.
|
||||
row = M + 1 - as.integer(substr(square, 2, 2))
|
||||
ascii = function (ch) { as.integer(charToRaw(ch)) }
|
||||
col = 1 + ascii(substr(square, 1, 1)) - ascii('a')
|
||||
position = c(row, col)
|
||||
|
||||
# Begin tour.
|
||||
setboard(position, 1); knightTour(position, 2)
|
||||
38
Task/Knights-tour/REXX/knights-tour.rexx
Normal file
38
Task/Knights-tour/REXX/knights-tour.rexx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*REXX program solves the knight's tour problem for a (general) NxN chessboard.*/
|
||||
parse arg N sRank sFile . /*obtain optional arguments from the CL*/
|
||||
if N=='' | N=="," then N=8 /*No boardsize specified? Use default.*/
|
||||
if sRank=='' | sRank=="," then sRank=N /*No starting rank given? " " */
|
||||
if sFile=='' | sFile=="," then sFile=1 /* " " file " " " */
|
||||
NN=N**2; NxN='a ' N"x"N ' chessboard' /*file [↓] [↓] r=rank */
|
||||
@.=; do r=1 for N; do f=1 for N; @.r.f=.; end /*f*/; end /*r*/
|
||||
beg= '-1-' /*[↑] create an empty NxN chessboard.*/
|
||||
Kr = '2 1 -1 -2 -2 -1 1 2' /*the legal "rank" moves for a knight.*/
|
||||
Kf = '1 2 2 1 -1 -2 -2 -1' /* " " "file" " " " " */
|
||||
kr.M=words(Kr) /*number of possible moves for a Knight*/
|
||||
parse var Kr Kr.1 Kr.2 Kr.3 Kr.4 Kr.5 Kr.6 Kr.7 Kr.8 /*parse the legal moves by hand*/
|
||||
parse var Kf Kf.1 Kf.2 Kf.3 Kf.4 Kf.5 Kf.6 Kf.7 Kf.8 /* " " " " " " */
|
||||
@.sRank.sFile= beg /*the knight's starting position. */
|
||||
@kt= "knight's tour" /*a handy-dandy literal for the SAYs. */
|
||||
if \move(2, sRank, sFile) & \(N==1) then say 'No' @kt "solution for" NxN'.'
|
||||
else say 'A solution for the' @kt "on" NxN':'
|
||||
!=left('', 9 * (n<18) ) /*used for indentation of chessboard. */
|
||||
_=substr(copies("┼───",N),2); say; say ! translate('┌'_"┐", '┬', "┼") /*a square.*/
|
||||
/* [↓] build a display for chessboard.*/
|
||||
do r=N for N by -1; if r\==N then say ! '├'_"┤"; L=@.
|
||||
do f=1 for N; ?=@.r.f; if ?==NN then ?='end'; L=L'│'center(?, 3) /*is "end"?*/
|
||||
end /*f*/ /*done with rank of the chessboard.*/
|
||||
say ! translate(L'│', , .) /*display a " " " " */
|
||||
end /*r*/ /*19x19 chessboard can be shown 80 cols*/
|
||||
|
||||
say ! translate('└'_"┘", '┴', "┼") /*show the last rank of the chessboard.*/
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
move: procedure expose @. Kr. Kf. NN; parse arg #,rank,file /*obtain move,rank,file.*/
|
||||
do t=1 for Kr.M; nr=rank+Kr.t; nf=file+Kf.t /*position of the knight*/
|
||||
if @.nr.nf==. then do; @.nr.nf=# /*Empty? Knight can move*/
|
||||
if #==NN then return 1 /*is this the last move?*/
|
||||
if move(#+1,nr,nf) then return 1 /* " " " " " */
|
||||
@.nr.nf=. /*undo the above move. */
|
||||
end /*try different move. */
|
||||
end /*t*/ /* [↑] all moves tried.*/
|
||||
return 0 /*tour is not possible. */
|
||||
24
Task/Knights-tour/Racket/knights-tour.rkt
Normal file
24
Task/Knights-tour/Racket/knights-tour.rkt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#lang racket
|
||||
(define N 8)
|
||||
(define nexts ; construct the graph
|
||||
(let ([ds (for*/list ([x 2] [x* '(+1 -1)] [y* '(+1 -1)])
|
||||
(cons (* x* (+ 1 x)) (* y* (- 2 x))))])
|
||||
(for*/vector ([i N] [j N])
|
||||
(filter values (for/list ([d ds])
|
||||
(let ([i (+ i (car d))] [j (+ j (cdr d))])
|
||||
(and (< -1 i N) (< -1 j N) (+ j (* N i)))))))))
|
||||
(define (tour x y)
|
||||
(define xy (+ x (* N y)))
|
||||
(let loop ([seen (list xy)] [ns (vector-ref nexts xy)] [n (sub1 (* N N))])
|
||||
(if (zero? n) (reverse seen)
|
||||
(for/or ([next (sort (map (λ(n) (cons n (remq* seen (vector-ref nexts n)))) ns)
|
||||
< #:key length #:cache-keys? #t)])
|
||||
(loop (cons (car next) seen) (cdr next) (sub1 n))))))
|
||||
(define (draw tour)
|
||||
(define v (make-vector (* N N)))
|
||||
(for ([n tour] [i (in-naturals 1)]) (vector-set! v n i))
|
||||
(for ([i N])
|
||||
(displayln (string-join (for/list ([j (in-range i (* N N) N)])
|
||||
(~a (vector-ref v j) #:width 2 #:align 'right))
|
||||
" "))))
|
||||
(draw (tour (random N) (random N)))
|
||||
73
Task/Knights-tour/Raku/knights-tour-1.raku
Normal file
73
Task/Knights-tour/Raku/knights-tour-1.raku
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
my @board;
|
||||
|
||||
my $I = 8;
|
||||
my $J = 8;
|
||||
my $F = $I*$J > 99 ?? "%3d" !! "%2d";
|
||||
|
||||
# Choose starting position - may be passed in on command line; if
|
||||
# not, choose random square.
|
||||
my ($i, $j);
|
||||
|
||||
if my $sq = shift @*ARGS {
|
||||
die "$*PROGRAM_NAME: illegal start square '$sq'\n" unless ($i, $j) = from_algebraic($sq);
|
||||
}
|
||||
else {
|
||||
($i, $j) = (^$I).pick, (^$J).pick;
|
||||
}
|
||||
|
||||
# Move sequence
|
||||
my @moves = ();
|
||||
|
||||
for 1 .. $I * $J -> $move {
|
||||
# Record current move
|
||||
push @moves, to_algebraic($i,$j);
|
||||
@board[$i][$j] = $move;
|
||||
|
||||
# Find move with the smallest degree
|
||||
my @min = (9);
|
||||
for possible_moves($i,$j) -> @target {
|
||||
my ($ni, $nj) = @target;
|
||||
my $next = possible_moves($ni,$nj);
|
||||
@min = $next, $ni, $nj if $next < @min[0];
|
||||
}
|
||||
|
||||
# And make it
|
||||
($i, $j) = @min[1,2];
|
||||
}
|
||||
|
||||
# Print the move list
|
||||
for @moves.kv -> $i, $m {
|
||||
print ',', $i %% 16 ?? "\n" !! " " if $i;
|
||||
print $m;
|
||||
}
|
||||
say "\n";
|
||||
|
||||
# And the board, with move numbers
|
||||
for ^$I -> $i {
|
||||
for ^$J -> $j {
|
||||
# Assumes (1) ANSI sequences work, and (2) output
|
||||
# is light text on a dark background.
|
||||
print "\e[7m" if $i % 2 == $j % 2;
|
||||
printf $F, @board[$i][$j];
|
||||
print "\e[0m";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Find the list of positions the knight can move to from the given square
|
||||
sub possible_moves($i,$j) {
|
||||
grep -> [$ni, $nj] { $ni ~~ ^$I and $nj ~~ ^$J and !@board[$ni][$nj] },
|
||||
[$i-2,$j-1], [$i-2,$j+1], [$i-1,$j-2], [$i-1,$j+2],
|
||||
[$i+1,$j-2], [$i+1,$j+2], [$i+2,$j-1], [$i+2,$j+1];
|
||||
}
|
||||
|
||||
# Return the algebraic name of the square identified by the coordinates
|
||||
# i=rank, 0=black's home row; j=file, 0=white's queen's rook
|
||||
sub to_algebraic($i,$j) {
|
||||
chr(ord('a') + $j) ~ ($I - $i);
|
||||
}
|
||||
|
||||
# Return the coordinates matching the given algebraic name
|
||||
sub from_algebraic($square where /^ (<[a..z]>) (\d+) $/) {
|
||||
$I - $1, ord(~$0) - ord('a');
|
||||
}
|
||||
650
Task/Knights-tour/Raku/knights-tour-2.raku
Normal file
650
Task/Knights-tour/Raku/knights-tour-2.raku
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# Find Knight’s Tours.
|
||||
#
|
||||
# Using Warnsdorff’s heuristic, find multiple solutions.
|
||||
# Optionally accept only closed tours.
|
||||
#
|
||||
# This program is migrated from my implementation for ATS/Postiats.
|
||||
# Arrays with dimension 1:64 take the place of stack frames.
|
||||
#
|
||||
# Compile with, for instance:
|
||||
#
|
||||
# ratfor77 knights_tour.r > knights_tour.f
|
||||
# gfortran -O2 -g -std=legacy -o knights_tour knights_tour.f
|
||||
#
|
||||
# or
|
||||
#
|
||||
# ratfor77 knights_tour.r > knights_tour.f
|
||||
# f2c knights_tour.f
|
||||
# cc -O -o knights_tour knights_tour.c -lf2c
|
||||
#
|
||||
# Usage examples:
|
||||
#
|
||||
# One tour starting at a1, either open or closed:
|
||||
#
|
||||
# echo "a1 1 F" | ./knights_tour
|
||||
#
|
||||
# No more than 2000 closed tours starting at c5:
|
||||
#
|
||||
# echo "c5 2000 T" | ./knights_tour
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
program ktour
|
||||
implicit none
|
||||
|
||||
character*2 alg
|
||||
integer i, j
|
||||
integer mxtour
|
||||
logical closed
|
||||
|
||||
read (*,*) alg, mxtour, closed
|
||||
call alg2ij (alg, i, j)
|
||||
call explor (i, j, mxtour, closed)
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine explor (istart, jstart, mxtour, closed)
|
||||
implicit none
|
||||
|
||||
# Explore the space of 'Warnsdorffian' knight’s paths, looking for
|
||||
# and printing complete tours.
|
||||
|
||||
integer istart, jstart # The starting position.
|
||||
integer mxtour # The maximum number of tours to print.
|
||||
logical closed # Closed tours only?
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
integer itours
|
||||
logical goodmv
|
||||
logical isclos
|
||||
|
||||
itours = 0
|
||||
call initbd (board)
|
||||
n = 1
|
||||
nmove(1) = 8
|
||||
imove(8, 1) = istart
|
||||
jmove(8, 1) = jstart
|
||||
|
||||
while (itours < mxtour && n != 0) {
|
||||
if (nmove(n) == 9) {
|
||||
n = n - 1
|
||||
if (n != 0) {
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
nmove(n) = nmove(n) + 1
|
||||
}
|
||||
} else if (goodmv (imove, nmove, n)) {
|
||||
call mkmove (board, imove, jmove, nmove, n)
|
||||
if (n == 64) {
|
||||
if (.not. closed) {
|
||||
itours = itours + 1
|
||||
call prnt (board, itours)
|
||||
} else if (isclos (board)) {
|
||||
itours = itours + 1
|
||||
call prnt (board, itours)
|
||||
}
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
nmove(n) = 9
|
||||
} else if (n == 63) {
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
n = n + 1
|
||||
nmove(n) = 1
|
||||
} else {
|
||||
call nxtmov (board, n, imove, jmove, nmove)
|
||||
n = n + 1
|
||||
nmove(n) = 1
|
||||
}
|
||||
} else {
|
||||
nmove(n) = nmove(n) + 1
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine initbd (board)
|
||||
implicit none
|
||||
|
||||
# Initialize a chessboard with empty squares.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
do j = 1, 8 {
|
||||
do i = 1, 8 {
|
||||
board(i, j) = -1
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine mkmove (board, imove, jmove, nmove, n)
|
||||
implicit none
|
||||
|
||||
# Fill a square with a move number.
|
||||
|
||||
integer board(1:8, 1:8)
|
||||
integer imove(1:8, 1:64)
|
||||
integer jmove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
board(imove(nmove(n), n), jmove(nmove(n), n)) = n
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine unmove (board, imove, jmove, nmove, n)
|
||||
implicit none
|
||||
|
||||
# Unmake a mkmove.
|
||||
|
||||
integer board(1:8, 1:8)
|
||||
integer imove(1:8, 1:64)
|
||||
integer jmove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
board(imove(nmove(n), n), jmove(nmove(n), n)) = -1
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
function goodmv (imove, nmove, n)
|
||||
implicit none
|
||||
|
||||
logical goodmv
|
||||
integer imove(1:8, 1:64)
|
||||
integer nmove(1:64)
|
||||
integer n
|
||||
|
||||
goodmv = (imove(nmove(n), n) != -1)
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine prnt (board, itours)
|
||||
implicit none
|
||||
|
||||
# Print a knight's tour.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer itours
|
||||
|
||||
10000 format (1X)
|
||||
|
||||
# The following plethora of format statements seemed a simple way to
|
||||
# get this working with f2c. (For gfortran, the 'I0' format
|
||||
# sufficed.)
|
||||
10010 format (1X, "Tour number ", I1)
|
||||
10020 format (1X, "Tour number ", I2)
|
||||
10030 format (1X, "Tour number ", I3)
|
||||
10040 format (1X, "Tour number ", I4)
|
||||
10050 format (1X, "Tour number ", I5)
|
||||
10060 format (1X, "Tour number ", I6)
|
||||
10070 format (1X, "Tour number ", I20)
|
||||
|
||||
if (itours < 10) {
|
||||
write (*, 10010) itours
|
||||
} else if (itours < 100) {
|
||||
write (*, 10020) itours
|
||||
} else if (itours < 1000) {
|
||||
write (*, 10030) itours
|
||||
} else if (itours < 10000) {
|
||||
write (*, 10040) itours
|
||||
} else if (itours < 100000) {
|
||||
write (*, 10050) itours
|
||||
} else if (itours < 1000000) {
|
||||
write (*, 10060) itours
|
||||
} else {
|
||||
write (*, 10070) itours
|
||||
}
|
||||
call prntmv (board)
|
||||
call prntbd (board)
|
||||
write (*, 10000)
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine prntbd (board)
|
||||
implicit none
|
||||
|
||||
# Print a chessboard with the move number in each square.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer i, j
|
||||
|
||||
10000 format (1X, " ", 8("+----"), "+")
|
||||
10010 format (1X, I2, " ", 8(" | ", I2), " | ")
|
||||
10020 format (1X, " ", 8(" ", A1))
|
||||
|
||||
do i = 8, 1, -1 {
|
||||
write (*, 10000)
|
||||
write (*, 10010) i, (board(i, j), j = 1, 8)
|
||||
}
|
||||
write (*, 10000)
|
||||
write (*, 10020) 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine prntmv (board)
|
||||
implicit none
|
||||
|
||||
# Print the moves of a knight's path, in algebraic notation.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
|
||||
integer ipos(1:64)
|
||||
integer jpos(1:64)
|
||||
integer numpos
|
||||
character*2 alg(1:64)
|
||||
integer columns(1:8)
|
||||
integer k
|
||||
integer m
|
||||
|
||||
character*72 lines(1:8)
|
||||
|
||||
10000 format (1X, A)
|
||||
|
||||
call bd2pos (board, ipos, jpos, numpos)
|
||||
|
||||
# Convert the positions to algebraic notation.
|
||||
do k = 1, numpos {
|
||||
call ij2alg (ipos(k), jpos(k), alg(k))
|
||||
}
|
||||
|
||||
# Fill lines with algebraic notations.
|
||||
do m = 1, 8 {
|
||||
columns(m) = 1
|
||||
}
|
||||
m = 1
|
||||
do k = 1, numpos {
|
||||
lines(m)(columns(m) : columns(m) + 1) = alg(k)(1:2)
|
||||
columns(m) = columns(m) + 2
|
||||
if (k != numpos) {
|
||||
lines(m)(columns(m) : columns(m) + 3) = " -> "
|
||||
columns(m) = columns(m) + 4
|
||||
} else if (numpos == 64 && _
|
||||
((abs (ipos(numpos) - ipos(1)) == 2 _
|
||||
&& abs (jpos(numpos) - jpos(1)) == 1) _
|
||||
|| ((abs (ipos(numpos) - ipos(1)) == 1 _
|
||||
&& abs (jpos(numpos) - jpos(1)) == 2)))) {
|
||||
lines(m)(columns(m) : columns(m) + 8) = " -> cycle"
|
||||
columns(m) = columns(m) + 9
|
||||
}
|
||||
if (mod (k, 8) == 0) m = m + 1
|
||||
}
|
||||
|
||||
# Print the lines that have stuff in them.
|
||||
do m = 1, 8 {
|
||||
if (columns(m) != 1) {
|
||||
write (*, 10000) lines(m)(1 : columns(m) - 1)
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
function isclos (board)
|
||||
implicit none
|
||||
|
||||
# Is a board a closed tour?
|
||||
|
||||
logical isclos
|
||||
integer board(1:8,1:8)
|
||||
integer ipos(1:64) # The i-positions in order.
|
||||
integer jpos(1:64) # The j-positions in order.
|
||||
integer numpos # The number of positions so far.
|
||||
|
||||
call bd2pos (board, ipos, jpos, numpos)
|
||||
|
||||
isclos = (numpos == 64 && _
|
||||
((abs (ipos(numpos) - ipos(1)) == 2 _
|
||||
&& abs (jpos(numpos) - jpos(1)) == 1) _
|
||||
|| ((abs (ipos(numpos) - ipos(1)) == 1 _
|
||||
&& abs (jpos(numpos) - jpos(1)) == 2))))
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine bd2pos (board, ipos, jpos, numpos)
|
||||
implicit none
|
||||
|
||||
# Convert from a board to a list of board positions.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer ipos(1:64) # The i-positions in order.
|
||||
integer jpos(1:64) # The j-positions in order.
|
||||
integer numpos # The number of positions so far.
|
||||
|
||||
integer i, j
|
||||
|
||||
numpos = 0
|
||||
do i = 1, 8 {
|
||||
do j = 1, 8 {
|
||||
if (board(i, j) != -1) {
|
||||
numpos = max (board(i, j), numpos)
|
||||
ipos(board(i, j)) = i
|
||||
jpos(board(i, j)) = j
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine nxtmov (board, n, imove, jmove, nmove)
|
||||
implicit none
|
||||
|
||||
# Find possible next moves. Prune and sort the moves according to
|
||||
# Warnsdorff's heuristic, keeping only those that have the minimum
|
||||
# number of legal following moves.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
|
||||
integer w1, w2, w3, w4, w5, w6, w7, w8
|
||||
integer w
|
||||
integer n1
|
||||
integer pickw
|
||||
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
|
||||
n1 = n + 1
|
||||
nmove(n1) = 1
|
||||
call countf (board, n1, imove, jmove, nmove, w1)
|
||||
nmove(n1) = 2
|
||||
call countf (board, n1, imove, jmove, nmove, w2)
|
||||
nmove(n1) = 3
|
||||
call countf (board, n1, imove, jmove, nmove, w3)
|
||||
nmove(n1) = 4
|
||||
call countf (board, n1, imove, jmove, nmove, w4)
|
||||
nmove(n1) = 5
|
||||
call countf (board, n1, imove, jmove, nmove, w5)
|
||||
nmove(n1) = 6
|
||||
call countf (board, n1, imove, jmove, nmove, w6)
|
||||
nmove(n1) = 7
|
||||
call countf (board, n1, imove, jmove, nmove, w7)
|
||||
nmove(n1) = 8
|
||||
call countf (board, n1, imove, jmove, nmove, w8)
|
||||
|
||||
w = pickw (w1, w2, w3, w4, w5, w6, w7, w8)
|
||||
|
||||
if (w == 0) {
|
||||
call disabl (imove(1, n1), jmove(1, n1))
|
||||
call disabl (imove(2, n1), jmove(2, n1))
|
||||
call disabl (imove(3, n1), jmove(3, n1))
|
||||
call disabl (imove(4, n1), jmove(4, n1))
|
||||
call disabl (imove(5, n1), jmove(5, n1))
|
||||
call disabl (imove(6, n1), jmove(6, n1))
|
||||
call disabl (imove(7, n1), jmove(7, n1))
|
||||
call disabl (imove(8, n1), jmove(8, n1))
|
||||
} else {
|
||||
if (w != w1) call disabl (imove(1, n1), jmove(1, n1))
|
||||
if (w != w2) call disabl (imove(2, n1), jmove(2, n1))
|
||||
if (w != w3) call disabl (imove(3, n1), jmove(3, n1))
|
||||
if (w != w4) call disabl (imove(4, n1), jmove(4, n1))
|
||||
if (w != w5) call disabl (imove(5, n1), jmove(5, n1))
|
||||
if (w != w6) call disabl (imove(6, n1), jmove(6, n1))
|
||||
if (w != w7) call disabl (imove(7, n1), jmove(7, n1))
|
||||
if (w != w8) call disabl (imove(8, n1), jmove(8, n1))
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine countf (board, n, imove, jmove, nmove, w)
|
||||
implicit none
|
||||
|
||||
# Count the number of moves possible after an nth move.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
integer w
|
||||
|
||||
logical goodmv
|
||||
integer n1
|
||||
|
||||
if (goodmv (imove, nmove, n)) {
|
||||
call mkmove (board, imove, jmove, nmove, n)
|
||||
call possib (board, n, imove, jmove, nmove)
|
||||
n1 = n + 1
|
||||
w = 0
|
||||
if (imove(1, n1) != -1) w = w + 1
|
||||
if (imove(2, n1) != -1) w = w + 1
|
||||
if (imove(3, n1) != -1) w = w + 1
|
||||
if (imove(4, n1) != -1) w = w + 1
|
||||
if (imove(5, n1) != -1) w = w + 1
|
||||
if (imove(6, n1) != -1) w = w + 1
|
||||
if (imove(7, n1) != -1) w = w + 1
|
||||
if (imove(8, n1) != -1) w = w + 1
|
||||
call unmove (board, imove, jmove, nmove, n)
|
||||
} else {
|
||||
# The nth move itself is impossible.
|
||||
w = 0
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
function pickw (w1, w2, w3, w4, w5, w6, w7, w8)
|
||||
implicit none
|
||||
|
||||
# From w1..w8, pick out the least nonzero value (or zero if they all
|
||||
# equal zero).
|
||||
|
||||
integer pickw
|
||||
integer w1, w2, w3, w4, w5, w6, w7, w8
|
||||
|
||||
integer w
|
||||
integer pickw1
|
||||
|
||||
w = 0
|
||||
w = pickw1 (w, w1)
|
||||
w = pickw1 (w, w2)
|
||||
w = pickw1 (w, w3)
|
||||
w = pickw1 (w, w4)
|
||||
w = pickw1 (w, w5)
|
||||
w = pickw1 (w, w6)
|
||||
w = pickw1 (w, w7)
|
||||
w = pickw1 (w, w8)
|
||||
|
||||
pickw = w
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
function pickw1 (u, v)
|
||||
implicit none
|
||||
|
||||
# A small function used by pickw.
|
||||
|
||||
integer pickw1
|
||||
integer u, v
|
||||
|
||||
if (v == 0) {
|
||||
pickw1 = u
|
||||
} else if (u == 0) {
|
||||
pickw1 = v
|
||||
} else {
|
||||
pickw1 = min (u, v)
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine possib (board, n, imove, jmove, nmove)
|
||||
implicit none
|
||||
|
||||
# Find moves that are possible from an nth-move position.
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer n
|
||||
integer imove(1:8,1:64)
|
||||
integer jmove(1:8,1:64)
|
||||
integer nmove(1:64)
|
||||
|
||||
integer i, j
|
||||
integer n1
|
||||
|
||||
i = imove(nmove(n), n)
|
||||
j = jmove(nmove(n), n)
|
||||
n1 = n + 1
|
||||
call trymov (board, i + 1, j + 2, imove(1, n1), jmove(1, n1))
|
||||
call trymov (board, i + 2, j + 1, imove(2, n1), jmove(2, n1))
|
||||
call trymov (board, i + 1, j - 2, imove(3, n1), jmove(3, n1))
|
||||
call trymov (board, i + 2, j - 1, imove(4, n1), jmove(4, n1))
|
||||
call trymov (board, i - 1, j + 2, imove(5, n1), jmove(5, n1))
|
||||
call trymov (board, i - 2, j + 1, imove(6, n1), jmove(6, n1))
|
||||
call trymov (board, i - 1, j - 2, imove(7, n1), jmove(7, n1))
|
||||
call trymov (board, i - 2, j - 1, imove(8, n1), jmove(8, n1))
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine trymov (board, i, j, imove, jmove)
|
||||
implicit none
|
||||
|
||||
# Try a move to square (i, j).
|
||||
|
||||
integer board(1:8,1:8)
|
||||
integer i, j
|
||||
integer imove, jmove
|
||||
|
||||
call disabl (imove, jmove)
|
||||
if (1 <= i && i <= 8 && 1 <= j && j <= 8) {
|
||||
if (board(i,j) == -1) {
|
||||
call enable (i, j, imove, jmove)
|
||||
}
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine enable (i, j, imove, jmove)
|
||||
implicit none
|
||||
|
||||
# Enable a potential move.
|
||||
|
||||
integer i, j
|
||||
integer imove, jmove
|
||||
|
||||
imove = i
|
||||
jmove = j
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine disabl (imove, jmove)
|
||||
implicit none
|
||||
|
||||
# Disable a potential move.
|
||||
|
||||
integer imove, jmove
|
||||
|
||||
imove = -1
|
||||
jmove = -1
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine alg2ij (alg, i, j)
|
||||
implicit none
|
||||
|
||||
# Convert, for instance, 'c5' to i=3,j=5.
|
||||
|
||||
character*2 alg
|
||||
integer i, j
|
||||
|
||||
if (alg(1:1) == 'a') j = 1
|
||||
if (alg(1:1) == 'b') j = 2
|
||||
if (alg(1:1) == 'c') j = 3
|
||||
if (alg(1:1) == 'd') j = 4
|
||||
if (alg(1:1) == 'e') j = 5
|
||||
if (alg(1:1) == 'f') j = 6
|
||||
if (alg(1:1) == 'g') j = 7
|
||||
if (alg(1:1) == 'h') j = 8
|
||||
|
||||
if (alg(2:2) == '1') i = 1
|
||||
if (alg(2:2) == '2') i = 2
|
||||
if (alg(2:2) == '3') i = 3
|
||||
if (alg(2:2) == '4') i = 4
|
||||
if (alg(2:2) == '5') i = 5
|
||||
if (alg(2:2) == '6') i = 6
|
||||
if (alg(2:2) == '7') i = 7
|
||||
if (alg(2:2) == '8') i = 8
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
subroutine ij2alg (i, j, alg)
|
||||
implicit none
|
||||
|
||||
# Convert, for instance, i=3,j=5 to 'c5'.
|
||||
|
||||
integer i, j
|
||||
character*2 alg
|
||||
|
||||
character alg1
|
||||
character alg2
|
||||
|
||||
if (j == 1) alg1 = 'a'
|
||||
if (j == 2) alg1 = 'b'
|
||||
if (j == 3) alg1 = 'c'
|
||||
if (j == 4) alg1 = 'd'
|
||||
if (j == 5) alg1 = 'e'
|
||||
if (j == 6) alg1 = 'f'
|
||||
if (j == 7) alg1 = 'g'
|
||||
if (j == 8) alg1 = 'h'
|
||||
|
||||
if (i == 1) alg2 = '1'
|
||||
if (i == 2) alg2 = '2'
|
||||
if (i == 3) alg2 = '3'
|
||||
if (i == 4) alg2 = '4'
|
||||
if (i == 5) alg2 = '5'
|
||||
if (i == 6) alg2 = '6'
|
||||
if (i == 7) alg2 = '7'
|
||||
if (i == 8) alg2 = '8'
|
||||
|
||||
alg(1:1) = alg1
|
||||
alg(2:2) = alg2
|
||||
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
72
Task/Knights-tour/Ruby/knights-tour.rb
Normal file
72
Task/Knights-tour/Ruby/knights-tour.rb
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
class Board
|
||||
Cell = Struct.new(:value, :adj) do
|
||||
def self.end=(end_val)
|
||||
@@end = end_val
|
||||
end
|
||||
|
||||
def try(seq_num)
|
||||
self.value = seq_num
|
||||
return true if seq_num==@@end
|
||||
a = []
|
||||
adj.each_with_index do |cell, n|
|
||||
a << [wdof(cell.adj)*10+n, cell] if cell.value.zero?
|
||||
end
|
||||
a.sort.each {|_, cell| return true if cell.try(seq_num+1)}
|
||||
self.value = 0
|
||||
false
|
||||
end
|
||||
|
||||
def wdof(adj)
|
||||
adj.count {|cell| cell.value.zero?}
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(rows, cols)
|
||||
@rows, @cols = rows, cols
|
||||
unless defined? ADJACENT # default move (Knight)
|
||||
eval("ADJACENT = [[-1,-2],[-2,-1],[-2,1],[-1,2],[1,2],[2,1],[2,-1],[1,-2]]")
|
||||
end
|
||||
frame = ADJACENT.flatten.map(&:abs).max
|
||||
@board = Array.new(rows+frame) do |i|
|
||||
Array.new(cols+frame) do |j|
|
||||
(i<rows and j<cols) ? Cell.new(0) : nil # frame (Sentinel value : nil)
|
||||
end
|
||||
end
|
||||
rows.times do |i|
|
||||
cols.times do |j|
|
||||
@board[i][j].adj = ADJACENT.map{|di,dj| @board[i+di][j+dj]}.compact
|
||||
end
|
||||
end
|
||||
Cell.end = rows * cols
|
||||
@format = " %#{(rows * cols).to_s.size}d"
|
||||
end
|
||||
|
||||
def solve(sx, sy)
|
||||
if (@rows*@cols).odd? and (sx+sy).odd?
|
||||
puts "No solution"
|
||||
else
|
||||
puts (@board[sx][sy].try(1) ? to_s : "No solution")
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
(0...@rows).map do |x|
|
||||
(0...@cols).map{|y| @format % @board[x][y].value}.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def knight_tour(rows=8, cols=rows, sx=rand(rows), sy=rand(cols))
|
||||
puts "\nBoard (%d x %d), Start:[%d, %d]" % [rows, cols, sx, sy]
|
||||
Board.new(rows, cols).solve(sx, sy)
|
||||
end
|
||||
|
||||
knight_tour(8,8,3,1)
|
||||
|
||||
knight_tour(5,5,2,2)
|
||||
|
||||
knight_tour(4,9,0,0)
|
||||
|
||||
knight_tour(5,5,0,1)
|
||||
|
||||
knight_tour(12,12,1,1)
|
||||
111
Task/Knights-tour/Rust/knights-tour.rust
Normal file
111
Task/Knights-tour/Rust/knights-tour.rust
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
use std::fmt;
|
||||
|
||||
const SIZE: usize = 8;
|
||||
const MOVES: [(i32, i32); 8] = [
|
||||
(2, 1),
|
||||
(1, 2),
|
||||
(-1, 2),
|
||||
(-2, 1),
|
||||
(-2, -1),
|
||||
(-1, -2),
|
||||
(1, -2),
|
||||
(2, -1),
|
||||
];
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
fn mov(&self, &(dx, dy): &(i32, i32)) -> Self {
|
||||
Self {
|
||||
x: self.x + dx,
|
||||
y: self.y + dy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Board {
|
||||
field: [[i32; SIZE]; SIZE],
|
||||
}
|
||||
|
||||
impl Board {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
field: [[0; SIZE]; SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
fn available(&self, p: Point) -> bool {
|
||||
0 <= p.x
|
||||
&& p.x < SIZE as i32
|
||||
&& 0 <= p.y
|
||||
&& p.y < SIZE as i32
|
||||
&& self.field[p.x as usize][p.y as usize] == 0
|
||||
}
|
||||
|
||||
// calculate the number of possible moves
|
||||
fn count_degree(&self, p: Point) -> i32 {
|
||||
let mut count = 0;
|
||||
for dir in MOVES.iter() {
|
||||
let next = p.mov(dir);
|
||||
if self.available(next) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Board {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for row in self.field.iter() {
|
||||
for x in row.iter() {
|
||||
write!(f, "{:3} ", x)?;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn knights_tour(x: i32, y: i32) -> Option<Board> {
|
||||
let mut board = Board::new();
|
||||
let mut p = Point { x: x, y: y };
|
||||
let mut step = 1;
|
||||
board.field[p.x as usize][p.y as usize] = step;
|
||||
step += 1;
|
||||
|
||||
while step <= (SIZE * SIZE) as i32 {
|
||||
// choose next square by Warnsdorf's rule
|
||||
let mut candidates = vec![];
|
||||
for dir in MOVES.iter() {
|
||||
let adj = p.mov(dir);
|
||||
if board.available(adj) {
|
||||
let degree = board.count_degree(adj);
|
||||
candidates.push((degree, adj));
|
||||
}
|
||||
}
|
||||
match candidates.iter().min() {
|
||||
// move to next square
|
||||
Some(&(_, adj)) => p = adj,
|
||||
// can't move
|
||||
None => return None,
|
||||
};
|
||||
board.field[p.x as usize][p.y as usize] = step;
|
||||
step += 1;
|
||||
}
|
||||
Some(board)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (x, y) = (3, 1);
|
||||
println!("Board size: {}", SIZE);
|
||||
println!("Starting position: ({}, {})", x, y);
|
||||
match knights_tour(x, y) {
|
||||
Some(b) => print!("{}", b),
|
||||
None => println!("Fail!"),
|
||||
}
|
||||
}
|
||||
3
Task/Knights-tour/Scala/knights-tour.scala
Normal file
3
Task/Knights-tour/Scala/knights-tour.scala
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
val b=Seq.tabulate(8,8,8,8)((x,y,z,t)=>(1L<<(x*8+y),1L<<(z*8+t),f"${97+z}%c${49+t}%c",(x-z)*(x-z)+(y-t)*(y-t)==5)).flatten.flatten.flatten.filter(_._4).groupBy(_._1)
|
||||
def f(p:Long,s:Long,v:Any){if(-1L!=s)b(p).foreach(x=>if((s&x._2)==0)f(x._2,s|x._2,v+x._3))else println(v)}
|
||||
f(1,1,"a1")
|
||||
46
Task/Knights-tour/Scheme/knights-tour.ss
Normal file
46
Task/Knights-tour/Scheme/knights-tour.ss
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
;;/usr/bin/petite
|
||||
;;encoding:utf-8
|
||||
;;Author:Panda
|
||||
;;Mail:panbaoxiang@hotmail.com
|
||||
;;Created Time:Thu 29 Jan 2015 10:18:49 AM CST
|
||||
;;Description:
|
||||
|
||||
;;size of the chessboard
|
||||
(define X 8)
|
||||
(define Y 8)
|
||||
;;position is an integer that could be decoded into the x coordinate and y coordinate
|
||||
(define(decode position)
|
||||
(cons (div position Y) (remainder position Y)))
|
||||
;;record the paths and number of territories you have conquered
|
||||
(define dictionary '())
|
||||
(define counter 1)
|
||||
;;define the forbiddend territories(conquered and cul-de-sac)
|
||||
(define forbiddened '())
|
||||
;;renew when havn't conquered the world.
|
||||
(define (renew position)
|
||||
(define possible
|
||||
(let ((rules (list (+ (* 2 Y) 1 position)
|
||||
(+ (* 2 Y) -1 position)
|
||||
(+ (* -2 Y) 1 position)
|
||||
(+ (* -2 Y) -1 position)
|
||||
(+ Y 2 position)
|
||||
(+ Y -2 position)
|
||||
(- position Y 2)
|
||||
(- position Y -2))))
|
||||
(filter (lambda(x) (not (or (member x forbiddened) (< x 0) (>= x (* X Y))))) rules)))
|
||||
(if (null? possible)
|
||||
(begin (set! forbiddened (cons (car dictionary) forbiddened))
|
||||
(set! dictionary (cdr dictionary))
|
||||
(set! counter (- counter 1))
|
||||
(car dictionary))
|
||||
(begin (set! dictionary (cons (car possible) dictionary))
|
||||
(set! forbiddened dictionary)
|
||||
(set! counter (+ counter 1))
|
||||
(car possible))))
|
||||
;;go to search
|
||||
(define (go position)
|
||||
(if (= counter (* X Y))
|
||||
(begin
|
||||
(set! result (reverse dictionary))
|
||||
(display (map (lambda(x) (decode x)) result)))
|
||||
(go (renew position))))
|
||||
50
Task/Knights-tour/SequenceL/knights-tour.sequencel
Normal file
50
Task/Knights-tour/SequenceL/knights-tour.sequencel
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import <Utilities/Sequence.sl>;
|
||||
import <Utilities/Conversion.sl>;
|
||||
|
||||
main(args(2)) :=
|
||||
let
|
||||
N := stringToInt(args[1]) when size(args) > 0 else 8;
|
||||
M := stringToInt(args[2]) when size(args) > 1 else N;
|
||||
startX := stringToInt(args[3]) when size(args) > 2 else 1;
|
||||
startY := stringToInt(args[4]) when size(args) > 3 else 1;
|
||||
board[i,j] := 0 foreach i within 1 ... N, j within 1 ... M;
|
||||
spacing := size(toString(N*M)) + 1;
|
||||
in
|
||||
join(printRow(
|
||||
tour(setBoard(board, startX, startX, 1), [startX,startY], 2),
|
||||
spacing));
|
||||
|
||||
potentialMoves := [[2,1], [2,-1], [1,2], [1,-2], [-1,2], [-1,-2], [-2,1], [-2,-1]];
|
||||
|
||||
printRow(row(1), spacing) := join(printSquare(row, spacing)) ++ "\n";
|
||||
|
||||
printSquare(val, spacing) :=
|
||||
let
|
||||
str := toString(val);
|
||||
in
|
||||
duplicate(' ', spacing - size(str)) ++ str;
|
||||
|
||||
tour(board(2), current(1), move) :=
|
||||
let
|
||||
validMoves := validMove(board, current + potentialMoves);
|
||||
numMoves[i] := size(validMove(board, validMoves[i] + potentialMoves));
|
||||
chosenMove := minPosition(numMoves);
|
||||
in
|
||||
board when move > size(board) * size(board[1]) else
|
||||
[] when size(validMoves) = 0 else
|
||||
[] when move < size(board) * size(board[1]) and numMoves[chosenMove] = 0 else
|
||||
tour(setBoard(board, validMoves[chosenMove][1], validMoves[chosenMove][2], move), validMoves[chosenMove], move + 1);
|
||||
|
||||
validMove(board(2), position(1)) :=
|
||||
(position when board[position[1], position[2]] = 0)
|
||||
when position[1] >= 1 and position[1] <= size(board) and position[2] >= 1 and position[2] <= size(board);
|
||||
|
||||
minPosition(x(1)) := minPositionHelper(x, 2, 1, x[1]);
|
||||
minPositionHelper(x(1), i, minPos, minVal) :=
|
||||
minPos when i > size(x) else
|
||||
minPositionHelper(x, i + 1, minPos, minVal) when x[i] > minVal else
|
||||
minPositionHelper(x, i + 1, i, x[i]);
|
||||
|
||||
setBoard(board(2), x, y, value)[i,j] :=
|
||||
value when x = i and y = j else
|
||||
board[i,j] foreach i within 1 ... size(board), j within 1 ... size(board[1]);
|
||||
61
Task/Knights-tour/Sidef/knights-tour.sidef
Normal file
61
Task/Knights-tour/Sidef/knights-tour.sidef
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
var board = []
|
||||
var I = 8
|
||||
var J = 8
|
||||
var F = (I*J > 99 ? '%3d' : '%2d')
|
||||
|
||||
var (i, j) = (I.irand, J.irand)
|
||||
|
||||
func from_algebraic(square) {
|
||||
if (var match = square.match(/^([a-z])([0-9])\z/)) {
|
||||
return(I - Num(match[1]), match[0].ord - 'a'.ord)
|
||||
}
|
||||
die "Invalid block square: #{square}"
|
||||
}
|
||||
|
||||
func possible_moves(i,j) {
|
||||
gather {
|
||||
for ni,nj in [
|
||||
[i-2,j-1], [i-2,j+1], [i-1,j-2], [i-1,j+2],
|
||||
[i+1,j-2], [i+1,j+2], [i+2,j-1], [i+2,j+1],
|
||||
] {
|
||||
if ((ni ~~ ^I) && (nj ~~ ^J) && !board[ni][nj]) {
|
||||
take([ni, nj])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func to_algebraic(i,j) {
|
||||
('a'.ord + j).chr + Str(I - i)
|
||||
}
|
||||
|
||||
if (ARGV[0]) {
|
||||
(i, j) = from_algebraic(ARGV[0])
|
||||
}
|
||||
|
||||
var moves = []
|
||||
for move in (1 .. I*J) {
|
||||
moves << to_algebraic(i, j)
|
||||
board[i][j] = move
|
||||
var min = [9]
|
||||
for target in possible_moves(i, j) {
|
||||
var (ni, nj) = target...
|
||||
var nxt = possible_moves(ni, nj).len
|
||||
if (nxt < min[0]) {
|
||||
min = [nxt, ni, nj]
|
||||
}
|
||||
}
|
||||
|
||||
(i, j) = min[1,2]
|
||||
}
|
||||
|
||||
say (moves/4 -> map { .join(', ') }.join("\n") + "\n")
|
||||
|
||||
for i in ^I {
|
||||
for j in ^J {
|
||||
(i%2 == j%2) && print "\e[7m"
|
||||
F.printf(board[i][j])
|
||||
print "\e[0m"
|
||||
}
|
||||
print "\n"
|
||||
}
|
||||
130
Task/Knights-tour/Swift/knights-tour.swift
Normal file
130
Task/Knights-tour/Swift/knights-tour.swift
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
public struct CPoint {
|
||||
public var x: Int
|
||||
public var y: Int
|
||||
|
||||
public init(x: Int, y: Int) {
|
||||
(self.x, self.y) = (x, y)
|
||||
}
|
||||
|
||||
public func move(by: (dx: Int, dy: Int)) -> CPoint {
|
||||
return CPoint(x: self.x + by.dx, y: self.y + by.dy)
|
||||
}
|
||||
}
|
||||
|
||||
extension CPoint: Comparable {
|
||||
public static func <(lhs: CPoint, rhs: CPoint) -> Bool {
|
||||
if lhs.x == rhs.x {
|
||||
return lhs.y < rhs.y
|
||||
} else {
|
||||
return lhs.x < rhs.x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class KnightsTour {
|
||||
public var size: Int { board.count }
|
||||
|
||||
private var board: [[Int]]
|
||||
|
||||
public init(size: Int) {
|
||||
board = Array(repeating: Array(repeating: 0, count: size), count: size)
|
||||
}
|
||||
|
||||
public func countMoves(forPoint point: CPoint) -> Int {
|
||||
return KnightsTour.knightMoves.lazy
|
||||
.map(point.move)
|
||||
.reduce(0, {count, movedTo in
|
||||
return squareAvailable(movedTo) ? count + 1 : count
|
||||
})
|
||||
}
|
||||
|
||||
public func printBoard() {
|
||||
for row in board {
|
||||
for x in row {
|
||||
print("\(x) ", terminator: "")
|
||||
}
|
||||
|
||||
print()
|
||||
}
|
||||
|
||||
print()
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
for i in 0..<size {
|
||||
for j in 0..<size {
|
||||
board[i][j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func squareAvailable(_ p: CPoint) -> Bool {
|
||||
return 0 <= p.x
|
||||
&& p.x < size
|
||||
&& 0 <= p.y
|
||||
&& p.y < size
|
||||
&& board[p.x][p.y] == 0
|
||||
}
|
||||
|
||||
public func tour(startingAt point: CPoint = CPoint(x: 0, y: 0)) -> Bool {
|
||||
var step = 2
|
||||
var p = point
|
||||
|
||||
reset()
|
||||
|
||||
board[p.x][p.y] = 1
|
||||
|
||||
while step <= size * size {
|
||||
let candidates = KnightsTour.knightMoves.lazy
|
||||
.map(p.move)
|
||||
.map({moved in (moved, self.countMoves(forPoint: moved), self.squareAvailable(moved)) })
|
||||
.filter({ $0.2 })
|
||||
|
||||
guard let bestMove = candidates.sorted(by: bestChoice).first else {
|
||||
return false
|
||||
}
|
||||
|
||||
p = bestMove.0
|
||||
board[p.x][p.y] = step
|
||||
|
||||
step += 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func bestChoice(_ choice1: (CPoint, Int, Bool), _ choice2: (CPoint, Int, Bool)) -> Bool {
|
||||
if choice1.1 == choice2.1 {
|
||||
return choice1.0 < choice2.0
|
||||
}
|
||||
|
||||
return choice1.1 < choice2.1
|
||||
}
|
||||
|
||||
extension KnightsTour {
|
||||
fileprivate static let knightMoves = [
|
||||
(2, 1),
|
||||
(1, 2),
|
||||
(-1, 2),
|
||||
(-2, 1),
|
||||
(-2, -1),
|
||||
(-1, -2),
|
||||
(1, -2),
|
||||
(2, -1),
|
||||
]
|
||||
}
|
||||
|
||||
let b = KnightsTour(size: 8)
|
||||
|
||||
print()
|
||||
|
||||
let completed = b.tour(startingAt: CPoint(x: 3, y: 1))
|
||||
|
||||
if completed {
|
||||
print("Completed tour")
|
||||
} else {
|
||||
print("Did not complete tour")
|
||||
}
|
||||
|
||||
b.printBoard()
|
||||
107
Task/Knights-tour/Tcl/knights-tour-1.tcl
Normal file
107
Task/Knights-tour/Tcl/knights-tour-1.tcl
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package require Tcl 8.6; # For object support, which makes coding simpler
|
||||
|
||||
oo::class create KnightsTour {
|
||||
variable width height visited
|
||||
|
||||
constructor {{w 8} {h 8}} {
|
||||
set width $w
|
||||
set height $h
|
||||
set visited {}
|
||||
}
|
||||
|
||||
method ValidMoves {square} {
|
||||
lassign $square c r
|
||||
set moves {}
|
||||
foreach {dx dy} {-1 -2 -2 -1 -2 1 -1 2 1 2 2 1 2 -1 1 -2} {
|
||||
set col [expr {($c % $width) + $dx}]
|
||||
set row [expr {($r % $height) + $dy}]
|
||||
if {$row >= 0 && $row < $height && $col >=0 && $col < $width} {
|
||||
lappend moves [list $col $row]
|
||||
}
|
||||
}
|
||||
return $moves
|
||||
}
|
||||
|
||||
method CheckSquare {square} {
|
||||
set moves 0
|
||||
foreach site [my ValidMoves $square] {
|
||||
if {$site ni $visited} {
|
||||
incr moves
|
||||
}
|
||||
}
|
||||
return $moves
|
||||
}
|
||||
|
||||
method Next {square} {
|
||||
set minimum 9
|
||||
set nextSquare {-1 -1}
|
||||
foreach site [my ValidMoves $square] {
|
||||
if {$site ni $visited} {
|
||||
set count [my CheckSquare $site]
|
||||
if {$count < $minimum} {
|
||||
set minimum $count
|
||||
set nextSquare $site
|
||||
} elseif {$count == $minimum} {
|
||||
set nextSquare [my Edgemost $nextSquare $site]
|
||||
}
|
||||
}
|
||||
}
|
||||
return $nextSquare
|
||||
}
|
||||
|
||||
method Edgemost {a b} {
|
||||
lassign $a ca ra
|
||||
lassign $b cb rb
|
||||
# Calculate distances to edge
|
||||
set da [expr {min($ca, $width - 1 - $ca, $ra, $height - 1 - $ra)}]
|
||||
set db [expr {min($cb, $width - 1 - $cb, $rb, $height - 1 - $rb)}]
|
||||
if {$da < $db} {return $a} else {return $b}
|
||||
}
|
||||
|
||||
method FormatSquare {square} {
|
||||
lassign $square c r
|
||||
format %c%d [expr {97 + $c}] [expr {1 + $r}]
|
||||
}
|
||||
|
||||
method constructFrom {initial} {
|
||||
while 1 {
|
||||
set visited [list $initial]
|
||||
set square $initial
|
||||
while 1 {
|
||||
set square [my Next $square]
|
||||
if {$square eq {-1 -1}} {
|
||||
break
|
||||
}
|
||||
lappend visited $square
|
||||
}
|
||||
if {[llength $visited] == $height*$width} {
|
||||
return
|
||||
}
|
||||
puts stderr "rejecting path of length [llength $visited]..."
|
||||
}
|
||||
}
|
||||
|
||||
method constructRandom {} {
|
||||
my constructFrom [list \
|
||||
[expr {int(rand()*$width)}] [expr {int(rand()*$height)}]]
|
||||
}
|
||||
|
||||
method print {} {
|
||||
set s " "
|
||||
foreach square $visited {
|
||||
puts -nonewline "$s[my FormatSquare $square]"
|
||||
if {[incr i]%12} {
|
||||
set s " -> "
|
||||
} else {
|
||||
set s "\n -> "
|
||||
}
|
||||
}
|
||||
puts ""
|
||||
}
|
||||
|
||||
method isClosed {} {
|
||||
set a [lindex $visited 0]
|
||||
set b [lindex $visited end]
|
||||
expr {$a in [my ValidMoves $b]}
|
||||
}
|
||||
}
|
||||
8
Task/Knights-tour/Tcl/knights-tour-2.tcl
Normal file
8
Task/Knights-tour/Tcl/knights-tour-2.tcl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
set kt [KnightsTour new]
|
||||
$kt constructRandom
|
||||
$kt print
|
||||
if {[$kt isClosed]} {
|
||||
puts "This is a closed tour"
|
||||
} else {
|
||||
puts "This is an open tour"
|
||||
}
|
||||
8
Task/Knights-tour/Tcl/knights-tour-3.tcl
Normal file
8
Task/Knights-tour/Tcl/knights-tour-3.tcl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
set kt [KnightsTour new 7 7]
|
||||
$kt constructFrom {0 0}
|
||||
$kt print
|
||||
if {[$kt isClosed]} {
|
||||
puts "This is a closed tour"
|
||||
} else {
|
||||
puts "This is an open tour"
|
||||
}
|
||||
58
Task/Knights-tour/Wren/knights-tour.wren
Normal file
58
Task/Knights-tour/Wren/knights-tour.wren
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
class Square {
|
||||
construct new(x, y) {
|
||||
_x = x
|
||||
_y = y
|
||||
}
|
||||
|
||||
x { _x }
|
||||
y { _y }
|
||||
|
||||
==(other) { _x == other.x && _y == other.y }
|
||||
}
|
||||
|
||||
var board = List.filled(8 * 8, null)
|
||||
for (i in 0...board.count) board[i] = Square.new((i/8).floor + 1, i%8 + 1)
|
||||
var axisMoves = [1, 2, -1, -2]
|
||||
|
||||
var allPairs = Fn.new { |a|
|
||||
var pairs = []
|
||||
for (i in a) {
|
||||
for (j in a) pairs.add([i, j])
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
var knightMoves = Fn.new { |s|
|
||||
var moves = allPairs.call(axisMoves).where { |p| p[0].abs != p[1].abs }
|
||||
var onBoard = Fn.new { |s| board.any { |i| i == s } }
|
||||
return moves.map { |p| Square.new(s.x + p[0], s.y + p[1]) }.where(onBoard)
|
||||
}
|
||||
|
||||
var knightTour // recursive
|
||||
knightTour = Fn.new { |moves|
|
||||
var findMoves = Fn.new { |s|
|
||||
return knightMoves.call(s).where { |m| !moves.any { |m2| m2 == m } }.toList
|
||||
}
|
||||
var fm = findMoves.call(moves[-1])
|
||||
if (fm.isEmpty) return moves
|
||||
var lowest = findMoves.call(fm[0]).count
|
||||
var lowestIndex = 0
|
||||
for (i in 1...fm.count) {
|
||||
var count = findMoves.call(fm[i]).count
|
||||
if (count < lowest) {
|
||||
lowest = count
|
||||
lowestIndex = i
|
||||
}
|
||||
}
|
||||
var newSquare = fm[lowestIndex]
|
||||
return knightTour.call(moves + [newSquare])
|
||||
}
|
||||
|
||||
var knightTourFrom = Fn.new { |start| knightTour.call([start]) }
|
||||
|
||||
var col = 0
|
||||
for (p in knightTourFrom.call(Square.new(1, 1))) {
|
||||
System.write("%(p.x),%(p.y)")
|
||||
System.write((col == 7) ? "\n" : " ")
|
||||
col = (col + 1) % 8
|
||||
}
|
||||
51
Task/Knights-tour/XPL0/knights-tour.xpl0
Normal file
51
Task/Knights-tour/XPL0/knights-tour.xpl0
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
int Board(8+2+2, 8+2+2); \board array with borders
|
||||
int LegalX, LegalY; \arrays of legal moves
|
||||
def IntSize=4; \number of bytes in an integer (4 or 2)
|
||||
include c:\cxpl\codes; \intrinsic 'code' declarations
|
||||
|
||||
|
||||
func Try(I, X, Y); \Make a tentative move from X,Y
|
||||
int I, X, Y;
|
||||
int K, U, V;
|
||||
[for K:= 0 to 8-1 do \for all possible moves...
|
||||
[U:= X + LegalX(K); \U and V are next square
|
||||
V:= Y + LegalY(K);
|
||||
if Board(U,V) = 0 then \if square has not been visited then
|
||||
[Board(U,V):= I; \ mark square with sequence number
|
||||
if I = 8*8 then return true;
|
||||
if Try(I+1, U, V) then return true \led to solution?
|
||||
else Board(U,V):= 0; \no, undo tenative move
|
||||
];
|
||||
];
|
||||
return false;
|
||||
]; \Try
|
||||
|
||||
|
||||
int I, J;
|
||||
[LegalX:= [2, 1, -1, -2, -2, -1, 1, 2];
|
||||
LegalY:= [1, 2, 2, 1, -1, -2, -2, -1];
|
||||
|
||||
for J:= 0 to 8+2+2-1 do \set up surrounding border for speed
|
||||
for I:= 0 to 8+2+2-1 do
|
||||
Board(I,J):= 1;
|
||||
for J:= 0 to 8+2+2-1 do \reposition Board(0,0) to Board(2,2)
|
||||
Board(J):= Board(J) + 2*IntSize;
|
||||
Board:= Board + 2*IntSize;
|
||||
for J:= 0 to 8-1 do \empty board
|
||||
for I:= 0 to 8-1 do
|
||||
Board(I,J):= 0;
|
||||
Text(0, "Starting square (1-8,1-8): "); I:= IntIn(0)-1; J:= IntIn(0)-1;
|
||||
Board(I,J):= 1; \starting location is 0,0
|
||||
|
||||
if Try(2, I, J) then \try to find second square
|
||||
[for J:= 0 to 8-1 do \draw board with knight's move sequence
|
||||
[for I:= 0 to 8-1 do
|
||||
[if Board(I,J) < 10 then ChOut(0, ^ );
|
||||
IntOut(0, Board(I,J));
|
||||
ChOut(0, ^ );
|
||||
];
|
||||
CrLf(0);
|
||||
];
|
||||
]
|
||||
else Text(0, "No Solution.^M^J");
|
||||
]
|
||||
56
Task/Knights-tour/XSLT/knights-tour-1.xslt
Normal file
56
Task/Knights-tour/XSLT/knights-tour-1.xslt
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<xsl:package xsl:version="3.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:fn="http://www.w3.org/2005/xpath-functions"
|
||||
xmlns:tour="http://www.seanbdurkin.id.au/tour"
|
||||
name="tour:tours">
|
||||
<xsl:stylesheet>
|
||||
<xsl:function name="tour:manufacture-square"
|
||||
as="element(square)" visibility="public">
|
||||
<xsl:param name="rank" as="xs:integer" />
|
||||
<xsl:param name="file" as="xs:integer" />
|
||||
<square file="$file" rank="$rank" />
|
||||
</xsl:function>
|
||||
|
||||
<xsl:function name="tour:on-board" as="xs:boolean" visibility="public">
|
||||
<xsl:param name="rank" as="xs:integer" />
|
||||
<xsl:param name="file" as="xs:integer" />
|
||||
<xsl:copy-of select="($rank ge 1) and ($rank le 8) and
|
||||
($file ge 1) and ($file le 8)" />
|
||||
</xsl:function>
|
||||
|
||||
<xsl:function name="tour:solve-tour" as="item()*" visibility="public">
|
||||
<!-- Solves the tour for any specified piece. -->
|
||||
<!-- Outputs either a full solution of 64 squares, of if fail,
|
||||
a copy of the $state input. -->
|
||||
<xsl:param name="state" as="item()+" />
|
||||
<xsl:variable name="compute-possible-moves"
|
||||
select="$state[. instance of function(*)]"
|
||||
as="function(element(square)) as element(square)*">
|
||||
<xsl:variable name="way-points" select="$state/self::square" />
|
||||
<xsl:choose>
|
||||
<xsl:when test="count($way-points) eq 64">
|
||||
<xsl:sequence ="$state" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:sequence select="
|
||||
let $try-move := function( $state as item()*, $move as item()) as item()*)
|
||||
{
|
||||
if $state/self::square[@file=$move/@file]
|
||||
[@rank=$move/@rank]
|
||||
then $state
|
||||
else tour:solve-tour( ( $state, $move) )
|
||||
},
|
||||
$possible-moves := $compute-possible-moves( $way-points[last()])
|
||||
return if empty( $possible-moves) then $state
|
||||
else fn:fold-left( $try-move, $state, $possible-moves)" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable></xsl:function>
|
||||
</xsl:stylesheet>
|
||||
|
||||
<xsl:expose component="function"
|
||||
names="tour:manufacture-square tour:on-board tour:solve-tour"
|
||||
visibility="public" />
|
||||
|
||||
</xsl:package>
|
||||
38
Task/Knights-tour/XSLT/knights-tour-2.xslt
Normal file
38
Task/Knights-tour/XSLT/knights-tour-2.xslt
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<xsl:stylesheet version="3.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:fn="http://www.w3.org/2005/xpath-functions"
|
||||
xmlns:tour="http://www.seanbdurkin.id.au/tour"
|
||||
exclude-result-prefixes="xsl fn xs tour">
|
||||
<xsl:use-package name="tour:tours" />
|
||||
<xsl:output indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
|
||||
<xsl:mode on-no-match="shallow-copy" streamable="yes"/>
|
||||
|
||||
<xsl:template match="knight[square]">
|
||||
<xsl:variable name="error">
|
||||
<error>Failed to find solution to Knight's Tour.</error>
|
||||
</xsl:variable>
|
||||
<xsl:copy>
|
||||
<xsl:copy-of select="
|
||||
let $final-state := tour:solve-tour((
|
||||
function( $piece-position as element(square)) as element(square)*
|
||||
{ (: This function defines a knight's move. :)
|
||||
let $r0 := number( $piece-position/@rank),
|
||||
let $f0 := number( $piece-position/@file),
|
||||
for $r in -2..2, $f in -2..2 return
|
||||
if (abs($r) + abs($f) eq 3) and
|
||||
tour:on-board($r+$r0, $f+$f0) then
|
||||
tour:manufacture-square($r+$r0, $f+$f0)
|
||||
else ()
|
||||
}
|
||||
, current()/square)),
|
||||
$solution := $final-state/self::square
|
||||
return if count($solution) eq 64 then $solution
|
||||
else $error/*" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Add templates for other piece types if you want to solve
|
||||
their tours too. Solve by calling tour:solve-tour() . -->
|
||||
|
||||
</xsl:stylesheet>
|
||||
5
Task/Knights-tour/XSLT/knights-tour-3.xslt
Normal file
5
Task/Knights-tour/XSLT/knights-tour-3.xslt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<tt>
|
||||
<knight>
|
||||
<square file="1" rank="1" />
|
||||
</knight>
|
||||
</tt>
|
||||
8
Task/Knights-tour/XSLT/knights-tour-4.xslt
Normal file
8
Task/Knights-tour/XSLT/knights-tour-4.xslt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<tt>
|
||||
<knight>
|
||||
<square file="1" rank="1" />
|
||||
<square file="2" rank="3" />
|
||||
<square file="1" rank="5" />
|
||||
... etc for 64 squares.
|
||||
</knight>
|
||||
</tt>
|
||||
48
Task/Knights-tour/Zkl/knights-tour-1.zkl
Normal file
48
Task/Knights-tour/Zkl/knights-tour-1.zkl
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Use Warnsdorff's rule to perform a knights tour of a 8x8 board in
|
||||
// linear time.
|
||||
// See Pohl, Ira (July 1967),
|
||||
// "A method for finding Hamilton paths and Knight's tours"
|
||||
// http://portal.acm.org/citation.cfm?id=363463
|
||||
// Uses back tracking as a tie breaker (for the few cases in a 8x8 tour)
|
||||
class Board{
|
||||
var[const]deltas=[[(dx,dy); T(-2,2); T(-1,1); _]].extend(
|
||||
[[(dx,dy); T(-1,1); T(-2,2); _]]);
|
||||
fcn init{
|
||||
var board=L();
|
||||
(0).pump(64,board.append.fpM("1-",Void)); // fill board with Void
|
||||
}
|
||||
fcn idx(x,y) { x*8+y }
|
||||
fcn isMoveOK(x,y){ (0<=x<8) and (0<=y<8) and Void==board[idx(x,y)] }
|
||||
fcn gyrate(x,y,f){ // walk all legal moves from (a,b)
|
||||
deltas.pump(List,'wrap([(dx,dy)]){
|
||||
x+=dx; y+=dy; if(isMoveOK(x,y)) f(x,y); else Void.Skip
|
||||
});
|
||||
}
|
||||
fcn count(x,y){ n:=Ref(0); gyrate(x,y,n.inc); n.value }
|
||||
fcn moves(x,y){ gyrate(x,y,fcn(x,y){ T(x,y,count(x,y)) })}
|
||||
fcn knightsTour(x=0,y=0,n=1){ // using Warnsdorff's rule
|
||||
board[idx(x,y)]=n;
|
||||
while(m:=moves(x,y)){
|
||||
min:=m.reduce('wrap(pc,[(_,_,c)]){ (pc<c) and pc or c },9);
|
||||
m=m.filter('wrap([(_,_,c)]){ c==min }); // moves with same min moves
|
||||
if(m.len()>1){ // tie breaker time, may need to backtrack
|
||||
bs:=board.copy();
|
||||
if (64==m.pump(Void,'wrap([(a,b)]){
|
||||
board[idx(a,b)]=n;
|
||||
n2:=knightsTour(a,b,n+1);
|
||||
if (n2==64) return(Void.Stop,n2); // found a solution
|
||||
board=bs.copy();
|
||||
})) return(64);
|
||||
return(0);
|
||||
}
|
||||
else{
|
||||
x,y=m[0]; n+=1;
|
||||
board[idx(x,y)]=n;
|
||||
}
|
||||
} //while
|
||||
return(n);
|
||||
}
|
||||
fcn toString{ board.pump(String,T(Void.Read,7),
|
||||
fcn(ns){ vm.arglist.apply("%2s".fmt).concat(",")+"\n" });
|
||||
}
|
||||
}
|
||||
2
Task/Knights-tour/Zkl/knights-tour-2.zkl
Normal file
2
Task/Knights-tour/Zkl/knights-tour-2.zkl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
b:=Board(); b.knightsTour(3,3);
|
||||
b.println();
|
||||
2
Task/Knights-tour/Zkl/knights-tour-3.zkl
Normal file
2
Task/Knights-tour/Zkl/knights-tour-3.zkl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[[(x,y); [0..7]; [0..7];
|
||||
{ b:=Board(); n:=b.knightsTour(x,y); if(n!=64) b.println(">>>",x,",",y) } ]];
|
||||
Loading…
Add table
Add a link
Reference in a new issue