! RUBIK.f90
!
! FUNCTIONS:
! RUBIK - Entry point of console application.
!
!
!****************************************************************************
!
! PROGRAM: RUBIK
!
!
!****************************************************************************
module external_fcts
implicit none
integer :: m, n, o
contains
subroutine ToString(sequence_numbers, numbersToString)
integer, dimension(:), intent(in) :: sequence_numbers
integer :: a, taille_sequence
character, dimension(:),intent(out) :: numbersToString
taille_sequence = size(sequence_numbers)
do a = 1, taille_sequence
select case(mod(sequence_numbers(a),6))
case(0)
numbersToString(3*a-2) = 'F'
case(1)
numbersToString(3*a-2) = 'U'
case(2)
numbersToString(3*a-2) = 'R'
case(3)
numbersToString(3*a-2) = 'B'
case(4)
numbersToString(3*a-2) = 'D'
case(5)
numbersToString(3*a-2) = 'L'
end select
numbersToString(3*a-1) = ' '
if (sequence_numbers(a)> 5) numbersToString(3*a-1) = '2'
if (sequence_numbers(a)>11) numbersToString(3*a-1) = "'"
numbersToString(3*a) = ' '
end do
end subroutine ToString
subroutine ToNumbers(sequence_string, StringToNumbers)
character, dimension(2), intent(in) :: sequence_string
integer, intent(out) :: StringToNumbers
integer :: a, b, taille_string
do a=1, 2
select case(sequence_string(a))
case('F')
StringToNumbers = 0
case('U')
StringToNumbers = 1
case('R')
StringToNumbers = 2
case('B')
StringToNumbers = 3
case('D')
StringToNumbers = 4
case('L')
StringToNumbers = 5
case("2")
StringToNumbers = StringToNumbers+6
case("'")
StringToNumbers = StringToNumbers+12
!case default
! print*, 'Incorrect caracter. Exit and run the program again'
end select
end do
!print*, StringToNumbers
end subroutine ToNumbers
function init(cube)
integer, dimension(0:53), intent(in) :: cube
integer, dimension(0:53) :: init
integer :: indice = 0, temp = 0
do m = 0, 5
do n = 0, 8
init(indice) = temp
indice = indice+1
end do
temp = temp+1
end do
return
end function init
function do_move(cube_state, move)
integer, dimension(0:53), intent(in) :: cube_state
integer, dimension(0:53) :: do_move, cube
integer, intent(in) :: move
integer :: repetition
do_move = cube_state
repetition = 0
if(move > 5) repetition = 1
if(move > 11) repetition = 2
if(move > 17) repetition = 3 !stupide, c'est un tour complet (juste pour test)
do m = 0, repetition
cube = do_move
!tourne les cubes de la face en question
do_move = turn_face(do_move, mod(move,6))
!tourne ceux de la couronne
select case(mod(move,6))
case(0)
do_move(15) = cube(53)
do_move(16) = cube(50)
do_move(17) = cube(47)
do_move(18) = cube(15)
do_move(21) = cube(16)
do_move(24) = cube(17)
do_move(36) = cube(24)
do_move(37) = cube(21)
do_move(38) = cube(18)
do_move(47) = cube(36)
do_move(50) = cube(37)
do_move(53) = cube(38)
case(1)
do_move(0) = cube(18)
do_move(1) = cube(19)
do_move(2) = cube(20)
do_move(18) = cube(27)
do_move(19) = cube(28)
do_move(20) = cube(29)
do_move(27) = cube(45)
do_move(28) = cube(46)
do_move(29) = cube(47)
do_move(45) = cube(0)
do_move(46) = cube(1)
do_move(47) = cube(2)
case(2)
do_move(2) = cube(38)
do_move(5) = cube(41)
do_move(8) = cube(44)
do_move(11) = cube(2)
do_move(14) = cube(5)
do_move(17) = cube(8)
do_move(27) = cube(17)
do_move(30) = cube(14)
do_move(33) = cube(11)
do_move(38) = cube(33)
do_move(41) = cube(30)
do_move(44) = cube(27)
case(3)
do_move(9) = cube(20)
do_move(10) = cube(23)
do_move(11) = cube(26)
do_move(20) = cube(44)
do_move(23) = cube(43)
do_move(26) = cube(42)
do_move(42) = cube(45)
do_move(43) = cube(48)
do_move(44) = cube(51)
do_move(45) = cube(11)
do_move(48) = cube(10)
do_move(51) = cube(9)
case(4)
do_move(6) = cube(51)
do_move(7) = cube(52)
do_move(8) = cube(53)
do_move(24) = cube(6)
do_move(25) = cube(7)
do_move(26) = cube(8)
do_move(33) = cube(24)
do_move(34) = cube(25)
do_move(35) = cube(26)
do_move(51) = cube(33)
do_move(52) = cube(34)
do_move(53) = cube(35)
case(5)
do_move(0) = cube(9)
do_move(3) = cube(12)
do_move(6) = cube(15)
do_move(9) = cube(35)
do_move(12) = cube(32)
do_move(15) = cube(29)
do_move(29) = cube(42)
do_move(32) = cube(39)
do_move(35) = cube(36)
do_move(36) = cube(0)
do_move(39) = cube(3)
do_move(42) = cube(6)
case default
print*, "move error"
end select
end do
return
end function do_move
function turn_face(cube, face)
integer, dimension(0:53), intent(in) :: cube
integer, dimension(0:53) :: turn_face
integer, intent(in) :: face
integer :: offset
turn_face = cube
offset = 9*face
turn_face(mod(offset,54)) = cube(mod(offset+6,54))
turn_face(mod(offset+1,54)) = cube(mod(offset+3,54))
turn_face(mod(offset+2,54)) = cube(mod(offset+0,54))
turn_face(mod(offset+3,54)) = cube(mod(offset+7,54))
turn_face(mod(offset+5,54)) = cube(mod(offset+1,54))
turn_face(mod(offset+6,54)) = cube(mod(offset+8,54))
turn_face(mod(offset+7,54)) = cube(mod(offset+5,54))
turn_face(mod(offset+8,54)) = cube(mod(offset+2,54))
end function turn_face
subroutine print_cube(cube)
integer, intent(in), dimension(0:53) :: cube
integer :: offset
offset = 0
do m = 0, 5
print*, cube(0+offset:8+offset)
offset = offset+9
end do
end subroutine print_cube
function compare(cube1, cube2)
integer, dimension(0:53), intent(in) :: cube1, cube2
integer :: compare
compare = 0
do m = 0, 53
if(cube1(m) == cube2(m)) compare = compare +1
if(cube2(m) > 5) then
if (cube1(m) < 6) compare = compare+1 !sticker = 6=> couleur pas importante
end if
end do
end function compare
function do_sequence(cube, move_sequence, sequence_size)
integer, intent(in) :: sequence_size
integer, dimension(0:53), intent(in) :: cube
integer, dimension(sequence_size), intent(in) :: move_sequence
integer, dimension(0:53) :: do_sequence
do_sequence = do_move(cube, move_sequence(1)) !initialisation
if(sequence_size > 1) then
do n = 2, sequence_size
do_sequence = do_move(do_sequence, move_sequence(n))
end do
end if
end function do_sequence
function entropy(cube)
!calcule l'entropie du cube, définie comme (somme(CEP) + somme(ECeP))
!CEP = corner-edge pairs
!ECeP = edges-center pairs
!Ouaiiii c'est pas la vrai entropie puisque je la maximise ;)
integer, dimension(0:53), intent(in) :: cube
integer :: entropy
entropy = 0
!Corner-edges pairs
!FLU corner
if(cube(0) == cube(1) .and. cube(15)== cube(16)) entropy = entropy+1
if(cube(0) == cube(3) .and. cube(47)== cube(50)) entropy = entropy+1
if(cube(47) == cube(46) .and. cube(15) == cube(12)) entropy = entropy+1
!FRU corner
if(cube(2) == cube(5) .and. cube(18)== cube(21)) entropy = entropy+1
if(cube(2) == cube(1) .and. cube(17)== cube(16)) entropy = entropy+1
if(cube(17) == cube(14) .and. cube(18) == cube(19)) entropy = entropy+1
!FRD corner
if(cube(8) == cube(5) .and. cube(24)== cube(21)) entropy = entropy+1
if(cube(8) == cube(7) .and. cube(38)== cube(37)) entropy = entropy+1
if(cube(38) == cube(41) .and. cube(24) == cube(25)) entropy = entropy+1
!FLD corner
if(cube(6) == cube(3) .and. cube(53)== cube(50)) entropy = entropy+1
if(cube(6) == cube(7) .and. cube(36)== cube(37)) entropy = entropy+1
if(cube(36) == cube(39) .and. cube(53) == cube(52)) entropy = entropy+1
!BLU corner
if(cube(29) == cube(32) .and. cube(45)== cube(48)) entropy = entropy+1
if(cube(29) == cube(28) .and. cube(9)== cube(10)) entropy = entropy+1
if(cube(9) == cube(12) .and. cube(45) == cube(46)) entropy = entropy+1
!BRU corner
if(cube(27) == cube(28) .and. cube(11)== cube(10)) entropy = entropy+1
if(cube(27) == cube(30) .and. cube(20)== cube(23)) entropy = entropy+1
if(cube(11) == cube(14) .and. cube(20) == cube(19)) entropy = entropy+1
!BRD corner
if(cube(33) == cube(34) .and. cube(44)== cube(43)) entropy = entropy+1
if(cube(33) == cube(30) .and. cube(26)== cube(23)) entropy = entropy+1
if(cube(44) == cube(41) .and. cube(26) == cube(25)) entropy = entropy+1
!BLD corner
if(cube(35) == cube(34) .and. cube(42)== cube(43)) entropy = entropy+1
if(cube(35) == cube(32) .and. cube(51)== cube(48)) entropy = entropy+1
if(cube(42) == cube(39) .and. cube(51) == cube(52)) entropy = entropy+1
!Edges-centers pairs
!F
if(cube(4) == cube(1)) entropy = entropy+1
if(cube(4) == cube(3)) entropy = entropy+1
if(cube(4) == cube(5)) entropy = entropy+1
if(cube(4) == cube(7)) entropy = entropy+1
!U
if(cube(13) == cube(10)) entropy = entropy+1
if(cube(13) == cube(12)) entropy = entropy+1
if(cube(13) == cube(14)) entropy = entropy+1
if(cube(13) == cube(16)) entropy = entropy+1
!R
if(cube(22) == cube(19)) entropy = entropy+1
if(cube(22) == cube(21)) entropy = entropy+1
if(cube(22) == cube(23)) entropy = entropy+1
if(cube(22) == cube(25)) entropy = entropy+1
!B
if(cube(31) == cube(28)) entropy = entropy+1
if(cube(31) == cube(30)) entropy = entropy+1
if(cube(31) == cube(32)) entropy = entropy+1
if(cube(31) == cube(34)) entropy = entropy+1
!D
if(cube(40) == cube(37)) entropy = entropy+1
if(cube(40) == cube(39)) entropy = entropy+1
if(cube(40) == cube(41)) entropy = entropy+1
if(cube(40) == cube(43)) entropy = entropy+1
!L
if(cube(49) == cube(46)) entropy = entropy+1
if(cube(49) == cube(48)) entropy = entropy+1
if(cube(49) == cube(50)) entropy = entropy+1
if(cube(49) == cube(52)) entropy = entropy+1
end function entropy
function is2x2x3(cube)
!Voir commentaires fonction entropy ...
!fonction "vérifiant" l'état du 2x2x3 situé en BD en terme de CE et ECe pairs
!vaut 16 ssi le 2x2x3 est complet
integer, dimension(0:53), intent(in) :: cube
integer :: is2x2x3
is2x2x3 = 0
!Corner-edges pairs
!BRD corner
if(cube(33) == cube(34) .and. cube(44)== cube(43)) is2x2x3 = is2x2x3+1
if(cube(33) == cube(30) .and. cube(26)== cube(23)) is2x2x3 = is2x2x3+1
if(cube(44) == cube(41) .and. cube(26) == cube(25)) is2x2x3 = is2x2x3+1
!BLD corner
if(cube(35) == cube(34) .and. cube(42)== cube(43)) is2x2x3 = is2x2x3+1
if(cube(35) == cube(32) .and. cube(51)== cube(48)) is2x2x3 = is2x2x3+1
if(cube(42) == cube(39) .and. cube(51) == cube(52)) is2x2x3 = is2x2x3+1
!Edges-centers pairs
!R
!if(cube(22) == cube(19)) is2x2x3 = is2x2x3+1
!if(cube(22) == cube(21)) is2x2x3 = is2x2x3+1
if(cube(22) == cube(23)) is2x2x3 = is2x2x3+1
if(cube(22) == cube(25)) is2x2x3 = is2x2x3+1
!B
!if(cube(31) == cube(28)) is2x2x3 = is2x2x3+1
if(cube(31) == cube(30)) is2x2x3 = is2x2x3+1
if(cube(31) == cube(32)) is2x2x3 = is2x2x3+1
if(cube(31) == cube(34)) is2x2x3 = is2x2x3+1
!D
!if(cube(40) == cube(37)) is2x2x3 = is2x2x3+1
if(cube(40) == cube(39)) is2x2x3 = is2x2x3+1
if(cube(40) == cube(41)) is2x2x3 = is2x2x3+1
if(cube(40) == cube(43)) is2x2x3 = is2x2x3+1
!L
!if(cube(49) == cube(46)) is2x2x3 = is2x2x3+1
if(cube(49) == cube(48)) is2x2x3 = is2x2x3+1
!if(cube(49) == cube(50)) is2x2x3 = is2x2x3+1
if(cube(49) == cube(52)) is2x2x3 = is2x2x3+1
end function is2x2x3
function is2gen(cube)
!checks if a given cube is 2-gen solvable, according to two rules :
! correct edges orientation
! solvable position of corners (according to Sebastian Dumitrescu's demonstration)
! two different corners linkages must be computed !
! returns 1 if OK, 0 otherwise
!The given 2 faces are F & U
use dflib
integer, dimension(0:53), intent(in) :: cube
integer :: is2gen
integer, dimension(6) :: corners_pos
integer, dimension(6) :: corner_temp
integer :: edges_ori, co
is2gen = 0
edges_ori = 0
!checks the orientation of edges
if(cube(1) == cube(4) .or. cube(16) == cube(13)) edges_ori = edges_ori+1
if(cube(3) == cube(4) .or. cube(50) == cube(13)) edges_ori = edges_ori+1
if(cube(5) == cube(4) .or. cube(21) == cube(13)) edges_ori = edges_ori+1
if(cube(7) == cube(4) .or. cube(37) == cube(13)) edges_ori = edges_ori+1
if(cube(28) == cube(4) .or. cube(10) == cube(13)) edges_ori = edges_ori+1
if(cube(46) == cube(4) .or. cube(13) == cube(13)) edges_ori = edges_ori+1
if(cube(19) == cube(4) .or. cube(14) == cube(13)) edges_ori = edges_ori+1
if (edges_ori < 7) return
!corners :
!5 6 U face
!3 4
!1 2 F face
!checks the position of the corners: 1st test (link 5-1, 6-2, 3-4 and observe the pattern)
corner_temp(1) = 2**cube(6)+ 2**cube(36)+2**cube(53) !FDL positioned corner of cube
corner_temp(2) = 2**cube(8)+2**cube(38)+2**cube(24) !FDR
corner_temp(3) = 2**cube(0)+2**cube(15)+2**cube(47) !FUL
corner_temp(4) = 2**cube(2)+2**cube(17)+2**cube(18) !FUR
corner_temp(5) = 2**cube(29)+2**cube(9)+2**cube(45) !BUL
corner_temp(6) = 2**cube(27)+2**cube(11)+2**cube(20) !BUR
do co = 1, 6
if (corner_temp(co) == 2**cube(4)+2**cube(40)+2**cube(49)) corners_pos(co)=0
if (corner_temp(co) == 2**cube(4)+2**cube(40)+2**cube(22)) corners_pos(co)=1
if (corner_temp(co) == 2**cube(4)+2**cube(13)+2**cube(49)) corners_pos(co)=2
if (corner_temp(co) == 2**cube(4)+2**cube(13)+2**cube(22)) corners_pos(co)=2
if (corner_temp(co) == 2**cube(31)+2**cube(13)+2**cube(49)) corners_pos(co)=0
if (corner_temp(co) == 2**cube(31)+2**cube(13)+2**cube(22)) corners_pos(co)=1
end do
!print*, "corners positions", corners_pos
!on doit passer en scalaire pour le select case
co = 100000*corners_pos(1)+10000*corners_pos(2)+1000*corners_pos(3)+100*corners_pos(4)+10*corners_pos(5)+corners_pos(6)
select case(co)
case(12201,1221,11022,12120,10212,102210,110220,100122,102021,101202,210021,221001,211200,210102,212010,21102,2112,22011,21210,20121,120012,112002,122100,120201,121020,201120,220110,200211,201012,202101)
is2gen = 1
case default
return
end select
!2nd test (link 5-2, 6-4, 3-1 and observe the pattern)
do co = 1, 6
if (corner_temp(co) == 2**cube(4)+2**cube(40)+2**cube(49)) corners_pos(co)=0
if (corner_temp(co) == 2**cube(4)+2**cube(40)+2**cube(22)) corners_pos(co)=1
if (corner_temp(co) == 2**cube(4)+2**cube(13)+2**cube(49)) corners_pos(co)=0
if (corner_temp(co) == 2**cube(4)+2**cube(13)+2**cube(22)) corners_pos(co)=2
if (corner_temp(co) == 2**cube(31)+2**cube(13)+2**cube(49)) corners_pos(co)=1
if (corner_temp(co) == 2**cube(31)+2**cube(13)+2**cube(22)) corners_pos(co)=2
end do
!print*, "corners positions", corners_pos
!on doit passer en scalaire pour le select case
co = 100000*corners_pos(1)+10000*corners_pos(2)+1000*corners_pos(3)+100*corners_pos(4)+10*corners_pos(5)+corners_pos(6)
select case(co)
case(12201,1221,11022,12120,10212,102210,110220,100122,102021,101202,210021,221001,211200,210102,212010,21102,2112,22011,21210,20121,120012,112002,122100,120201,121020,201120,220110,200211,201012,202101)
is2gen = 1
case default
is2gen = 0
end select
end function is2gen
function fobj(cube, cube_solved, moves_sequence, sequence_size, type_func )
!La fonction objectif est définie comme nmax - x, où nmax est le nombre max de stickers OK
!en parcourant la séquence de moves et x le nombre de mouvements après lesquels on y arrive
!Exemple d'appel :
!print*, fobj(cube_scrambled, cube_solved, moves_sequence, size(moves_sequence),1)
integer, intent(in) :: sequence_size, type_func
integer, dimension(0:53), intent(in) :: cube, cube_solved
integer, dimension(sequence_size), intent(in) :: moves_sequence
integer, dimension(2) :: fobj
integer :: nmax, x, ni, i
integer, dimension(0:53) :: cube_temp
cube_temp = cube
x = 0
nmax = 0 !approche par paires
!nmax = compare(cube_temp, cube_solved) !approche par stickers
if (sequence_size > 1) then
do i = 1, sequence_size
cube_temp = do_move(cube_temp, moves_sequence(i))
select case(type_func)
case (1)
ni = compare(cube_temp, cube_solved) !Approche par stickers
case (2)
ni = entropy(cube_temp) !Approche par paires
case (3)
ni = is2x2x3(cube_temp) !Approche par 2x2x3 en BD
case (4)
ni = 10*is2gen(cube_temp) + 10*is2x2x3(cube_temp) !Phase getting into 2-gen
end select
if (ni > nmax) then
nmax = ni
x = i
end if
!print*, i ,ni, nmax, x
end do
end if
fobj(1) = 10*nmax -x
fobj(2) = x
end function fobj
subroutine solve_twogen_bourrin(cube_2gen, cube_solved)
!résoud un cube dans le groupe twogen de manière optimale (HTM)
integer, dimension(0:53), intent(in) :: cube_2gen, cube_solved
integer, dimension(0:24) :: sol
integer, dimension(3) :: m
integer, dimension(2) :: test_resolu
integer :: m1,m2,m3
sol=sol+3 !On remplit la sol du 2-gen avec des mouvements qui n'ont rien à y faire (initialisation
do m1=0,5
sol(0)=(m1/2)*6 + mod(m1,2) !0,1,6,7,12,13 OK
!print*, mod(m1,2), " sol(0) : ", sol(0)
test_resolu = fobj(cube_2gen, cube_solved, sol, 25, 1)
!print*, (test_resolu(1)+test_resolu(2))/10.
if((test_resolu(1)+test_resolu(2))/10. == 54) then
print*, "youpie 1 !"
print*, sol
GOTO 3001
end if
do m2=0,5 !C'est pas top, ainsi il ajoute de toute façon #m mouvements, changer les boucles...
sol(1)=(m2/2)*6 + mod(m2,2) !0,1,6,7,12,13 OK
test_resolu = fobj(cube_2gen, cube_solved, sol, 25, 1)
if((test_resolu(1)+test_resolu(2))/10. == 54) then
print*, "youpie 2 !"
print*, sol
GOTO 3001
end if
end do
end do
3001 end subroutine solve_twogen_bourrin
function classement_population(pop, val_fobj)
!classe une population selon les valeurs décroissantes de la fonction objectif
integer, dimension(:, :), intent(in) :: pop
integer, dimension(:) :: val_fobj !seulement la valeur de fobj
integer :: max_indice, taille, i
integer, dimension(size(pop, 1), size(pop,2)) :: classement_population
taille = size(pop, 1)
classement_population = pop
do i = 1, taille
max_indice = maxloc(val_fobj,1)
classement_population(i,:) = pop(max_indice, :)
val_fobj(max_indice) = -10000
end do
end function classement_population
function pop_mariee (pop, phi, pc, perfo)
!Choisit des couples parmi une population d'individus triés par ordre de fobj croissante:
!pc = probabilité de croisement
!n = taille population
!phi = nbre moyen de descendants de l'Elite
use dflib
use dfport
integer, dimension(:,:), intent(in) :: pop
integer, dimension(size(pop,1),2), intent(in) :: perfo
real, intent(in) :: phi, pc
integer, dimension(size(pop,1),size(pop,2)) :: pop_mariee
real, dimension(0:size(pop,1)-1) :: pi
integer :: i, j, n, numero_enf, r0, p1, p2, cross
real :: x0, y0
call seed(RND$TIMESEED)
n = size(pop, 1)
!pop2 se remplira avec les enfants
pop_mariee = pop
!Calcul de la proba du nombre d'enfants en fct du rang
do i = 0, n-1
pi(i) = 1./n * (phi - i*(2.*phi-2)/(n-1.))
end do
!Stratégie élitiste : on conserve l'Elite -> boucle start @ numero_enf = 2 et génère (n-1)/2 couples
numero_enf = 2
do j = 1, (n-1)/2
!Choix du premier parent :
1 call random(x0)
r0 = int(n*0.9999*x0) !le 0.9999 c'est pour si jamais x0==1
call random(y0)
if( (2. - phi + 2.*y0*(phi-1.))/n > pi(r0)) then
GOTO 1
end if
p1 = r0+1
!Choix du 2e parent : aléatoire équiprobable
call random(x0)
p2=int(n*0.9999*x0)+1
!print*, p1, p2
!Choix du gène de cross-over appartient à {1, ..., long_code-1}
!ALEATOIRE
call random(x0)
cross = (size(pop,2)-1) * 0.9999* x0 + 1 !pour le cas foireux ou x0 ==1 pile
!OU : APRES LE 1ER MAX DE FOBJ pour le premier parent
!cross = perfo(p1, 2)
if (cross == size(pop,2)) cross = cross-1
!On attribue les gênes 1:cross du premier parent à l'individu numero_enf et cross+1:fin du 2e parent
!vice-versa à l'individu suivant (son frère)
!croisement a lieu avec une proba pc
call random(x0)
if(x0 < pc) then
pop_mariee(numero_enf, 1:cross) = pop(p1, 1:cross)
pop_mariee(numero_enf, cross+1:size(pop,2)) = pop(p2, cross+1:size(pop,2))
pop_mariee(numero_enf+1, 1:cross) = pop(p2, 1:cross)
pop_mariee(numero_enf+1, cross+1:size(pop,2)) = pop(p1, cross+1:size(pop,2))
else !pas croisement
pop_mariee(numero_enf, :) = pop(p1, :)
pop_mariee(numero_enf+1, :) = pop(p2, :)
end if
numero_enf =numero_enf + 2
end do !boucle j sur le remplissage de pop2
end function pop_mariee
function pop_mutee(pop, pm)
use dflib
use dfport
integer, dimension(:,:), intent(in) :: pop
real, intent(in) :: pm
integer, dimension(size(pop,1),size(pop,2)) :: pop_mutee
integer :: i, j, n1, n2, g0
real :: x0
call seed(RND$TIMESEED)
n1 = size(pop, 1)
n2 = size(pop, 2)
pop_mutee = pop
do i = 2, n1 !elite ne mute pas !
do j = 1, n2
call random(x0)
if (x0 < pm) then
call random(x0)
g0 = 18*0.9999*x0
pop_mutee(i,j) = g0
end if
end do
end do
end function pop_mutee
function trim_sequence (moves_sequence)
use dflib
use dfport
integer, dimension(:), intent(in) :: moves_sequence
integer, dimension(size(moves_sequence)) :: trim_sequence
integer :: l, ltot, shoots, move, move_suivant
real :: r
call seed(RND$TIMESEED)
ltot = size(moves_sequence)
trim_sequence = moves_sequence
shoots = 0
21 do l = 1, ltot-1
move = trim_sequence(l)
move_suivant = trim_sequence(l+1)
if(mod(move, 6) == mod(move_suivant, 6)) then
if(abs(move-move_suivant)== 12) then !Deux moves s'annulent
trim_sequence(l:ltot-2) = trim_sequence(l+2:ltot)
do shoots = 1,2
call random(r)
trim_sequence(ltot-2+shoots) = 18 * 0.9999* r
end do
end if
if(move < 6) then !Face
if(move_suivant == move) then ! face puis face = face^2
trim_sequence(l) = move+6
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
else if(move_suivant == move+6) then ! face puis face^2 = face'
trim_sequence(l) = move+12
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
end if
end if
if(move < 12) then
if(move<6) GOTO 22
if (move_suivant == move-6) then !Face^2 puis face = face'
trim_sequence(l) = move+6
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
else if (move_suivant == move) then !Face^2 puis face^2 = annule
trim_sequence(l:ltot-2) = trim_sequence(l+2:ltot)
do shoots = 1,2
call random(r)
trim_sequence(ltot-2+shoots) = 18 * 0.9999* r
end do
else if (move_suivant == move+6) then !Face^2 puis face' = face
trim_sequence(l) = move-6
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
end if
end if
if (move < 12) GOTO 22
! Arrive ci-dessous si et seulement move et face'
if(move_suivant == move-6) then !face' puis face^2 = face
trim_sequence(l) = move-12
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
else if(move_suivant == move) then !face' puis face' = face^2
trim_sequence(l) = move-6
trim_sequence(l+1:ltot-1) = trim_sequence(l+2:ltot)
call random(r)
trim_sequence(ltot) = 18 * 0.9999* r
end if
22 GO TO 21 ! Juste pour avoir un GO TO
end if
end do
end function trim_sequence
function twist_sequence(moves_sequence)
!twists moves sequence clockwise around FUR corner
!vouaiii un peu stupide, autant twister le cube ...
integer, dimension(:), intent(in) :: moves_sequence
integer, dimension(size(moves_sequence)):: twist_sequence
twist_sequence = 6*(moves_sequence/6) + mod(1+moves_sequence-6*(moves_sequence/6) ,3) + 3*((moves_sequence-6*(moves_sequence/6))/3)
end function twist_sequence
function twist_cube(c)
!twists whole cube around FUR corner
integer, dimension(0:53), intent(in) :: c
integer, dimension(0:53) :: twist_cube
!print*, "FUR clockwise twist (= x y)"
twist_cube(0:8) = [c(24),c(21),c(18),c(25),c(22),c(19),c(26),c(23),c(20)] !F<-R
twist_cube(9:17) = [c(6),c(3),c(0),c(7),c(4),c(1),c(8),c(5),c(2)] !U<-F
twist_cube(18:26) = [c(17),c(16),c(15),c(14),c(13),c(12),c(11),c(10),c(9)] !R<-U
twist_cube(27:35) = [c(47),c(50),c(53),c(46),c(49),c(52),c(45),c(48),c(51)] !B<-L
twist_cube(36:44) = [c(33),c(30),c(27),c(34),c(31),c(28),c(35),c(32),c(29)] !D<-B
twist_cube(45:53) = c(36:44) !L<-D
end function twist_cube
function rotate_whole_cube(cu)
!rotates whole cube clockwise around vertical axis
integer, dimension(0:53), intent(in) :: cu
integer, dimension(0:53) :: rotate_whole_cube
!print*, "whole cube clockwise rotation around Oz (= y)"
rotate_whole_cube(9:17) = [cu(15),cu(12),cu(9),cu(16),cu(13),cu(10),cu(17),cu(14),cu(11)] !U
rotate_whole_cube(36:44)= [cu(38),cu(41),cu(44),cu(37),cu(40),cu(43),cu(36),cu(39),cu(42)]!D
rotate_whole_cube(0:8) = cu(18:26) !F<-R
rotate_whole_cube(18:26) = cu(27:35) !R<-B
rotate_whole_cube(27:35) = cu(45:53) !B<-L
rotate_whole_cube(45:53) = cu(0:8) !L<-F
end function rotate_whole_cube
function rotate_whole_cube_x(cu)
!rotates whole cube clockwise LR axis (= x)
!defined as xy yyy
integer, dimension(0:53), intent(in) :: cu
integer, dimension(0:53) :: rotate_whole_cube_x
integer :: rot
!print*, "whole cube clockwise rotation around Oy (= x)"
rotate_whole_cube_x = cu
rotate_whole_cube_x = twist_cube(cu)
do rot = 1,3
rotate_whole_cube_x = rotate_whole_cube(rotate_whole_cube_x)
end do
end function rotate_whole_cube_x
end module external_fcts
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
program RUBIK
use external_fcts
use dflib
use dfport
implicit none
integer :: i, j, k, ll, lll, two_gen, loc2x2x3, scramble_type, moves_total, twogen_bourrin
integer, dimension(:), allocatable :: moves_sequence, scramble_sequence
integer, dimension(0:53) :: cube_solved, cube_scrambled, cube, cube_scrambled_copy
real :: r !random number
character, dimension(:),allocatable :: scramble_notation, sequence_notation, sol1, sol2, sol3
character*2 :: char_buffer
integer, dimension(12) :: best2x2x3loc, best2x2x3fobj, best2x2x3fobj_copie
integer, dimension(12, 30) :: best2x2x3elits
integer, dimension(12,2) :: best_temp
!Paramètres algo génétique
integer :: taille_pop, long_code, Ttot, generation, nbre_intrus, is_continuing
real :: pm, pc, phi
integer, dimension(:,:), allocatable :: pop, perfo, perfo_copie
integer, dimension(:,:,:), allocatable :: pop_storage !si on doit recommencer les 2x2x3, on ne perd pas tout ;-)
!initialisation
call seed(RND$TIMESEED) !la seed est effectivement différente à chaque appel :)
cube_solved = init(cube)
scramble_type = 2 !0 = défini manuellement, 1 = random, 2 = entré manuellement
print*, 'Genetic rubik''s cube solver, version 2.1'
print*, ''
print*, 'Cyril Castella, May 22, 2005'
print*, 'http://www.francocube.com'
print*, ''
print*, 'Enter scramble type : 1 = random, 2 = User-defined'
read*, scramble_type
print*, ''
print*, 'Enter number of generations for simulation : '
read*, Ttot
two_gen = 0
twogen_bourrin=0 !résoud le 2-gen par brute force au lieu algo génétique
open(unit=10, file='results.txt', status='replace')
!mélange
allocate(scramble_sequence(37))
!scramble_sequence = [6,1,14,17,0,1,14,17,0,1,14,17,0,1,14,17,0,9,13,14,10,0,2,9,7,0,2,9,7,0,2,9,7,0,2,9,7,4] !FMC#81, 38 moves
!scramble_sequence = [14,16,14,6,3,7,17,16,0,7,17,16,0,7,17,16,0,8,1,5,0,7,14,5,9,12,2,10,1,12,2,10,1,12,2,10,1,11,12,15,4] !41 moves
!scramble_sequence = [0,1,6,13,0,1,6,13,0,1,6,13,6,7,12,1,6,13,0,1,6,13,0,1,6,13,0,7,12,1,6,13,12,7,0,13,12,7,0,13,12,7,0] !43 moves 2-.gen
!scramble_sequence = [10,14,9,10,17,6,15,10,12,17,6,15,10,12,17,6,15,10,12,13,3,7,15,12,2,12,3,1,10,2,12,3,1,10,2,12,3,1,10,0,1,15,12] !#82, 43 moves
scramble_sequence = [2,4,2,3,11,4,15,5,0,4,15,5,0,4,15,5,0,8,4,12,7,3,2,16,7,3,2,16,7,3,2,16,11,2,4,7,6] !FMC#83, 37 moves
!if(two_gen) scramble_sequence = mod(scramble_sequence,2) + 6*(scramble_sequence/6) !2-gen scramble
if(scramble_type == 1) then
print*, 'Enter number of moves of the scramble'
read*, j
deallocate(scramble_sequence)
allocate(scramble_sequence(j))
do i = 1, j
call random(r)
scramble_sequence(i) = 18*0.9999*r
end do
scramble_sequence = trim_sequence(scramble_sequence)
endif
if(scramble_type ==2) then
print*, 'Enter number of moves of the scramble'
read*, j
print*, 'Enter the scramble moves. Only one move (HTM) per line !'
deallocate(scramble_sequence)
allocate(scramble_sequence(j))
do i = 1, j
read*, char_buffer
call ToNumbers(char_buffer, scramble_sequence(i))
end do
end if
allocate(scramble_notation(3*size(scramble_sequence)))
call ToString(scramble_sequence, scramble_notation)
print*, scramble_notation
!Ecriture du scramble dans le fichier de résultats
write(10, *), 'Scramble : '
write(10, *), scramble_notation
cube_scrambled = do_sequence(cube_solved, scramble_sequence, size(scramble_sequence))
cube_scrambled_copy = cube_scrambled
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Solving 2x2x3 !!!!!!!!!!!!!!!!!!!!!!!!!!!
is_continuing = 0
taille_pop = 51 !# individus
nbre_intrus = 5 !#individus aléatoires remplacent les plus mauvais de pop à chaque gén.
long_code = 30 !nombre de gènes
!Ttot = 50 !nombre de générations
pm = 0.05 !proba mutation
pc = 0.8 !proba croisement
phi = 1.5 !poids élite
allocate(pop_storage(12, taille_pop, long_code))
allocate(pop(taille_pop, long_code))
allocate(perfo(taille_pop, 2))
allocate(perfo_copie(taille_pop,2))
101 do loc2x2x3 = 0,11
print*,''
print*, '------------2x2x3 position is : ', loc2x2x3, ' ----------'
cube_scrambled = cube_scrambled_copy !subtil ... je crois qu'une de mes fcts change cube_scrambled
!Subtil ... mais visite effectivement toutes les 12 locations possibles ;-)
! 0 = BD, 1 = xy = BL, 2 = x2 = FU, 3 = y = DL, 4 = yxy = FL, 5 = yx2 = UR
! 6 = y2 = FD, 7 = y2xy = FR, 8 = y2x2 = UB, 9 = y3 = RD, 10 = y3xy = RB, 11 = y3x2UL
do ll=1, loc2x2x3/3
cube_scrambled = rotate_whole_cube(cube_scrambled)
print*, "whole cube clockwise rotation around Oz (= y)"
print*
end do
select case (modulo(loc2x2x3,3))
case(1)
cube_scrambled = twist_cube(cube_scrambled)
print*, "whole cube rotation around FUR corner (=xy)"
case(2)
do i = 1,2
cube_scrambled = rotate_whole_cube_x(cube_scrambled)
print*, "whole cube rotation around LR horizontal axis (=x)"
end do
end select
k = is2gen(cube_scrambled)
!print*, "... is 2-gen ? ", k
if(k==1) then
print*, '2-gen solve'
two_gen = 1
end if
!génération population initiale
do i = 1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
!si on est en train de continuer car pas assez de générations, on reprend la pop d'avant pour
!cette location
if(is_continuing) pop = pop_storage(loc2x2x3+1, :, :)
do generation = 1, Ttot
if(two_gen) pop = mod(pop,2) + 6*(pop/6) !2-gen-> séquence contient uniquement (F,U)
!Raccourci des mouvements qui s'annulent
do i = 1, taille_pop
pop(i,:) = trim_sequence(pop(i,:))
end do
!Evaluation de la fonction objectif sur la population
!pour fobj : 3=2x2x3, 4=is2gen, 2=entropy
do i = 1, taille_pop
perfo(i, :) = fobj(cube_scrambled, cube_solved, pop(i, :), long_code, 3)
!print*, pop(i,:), perfo(i, 1)
end do
!classement de la population
perfo_copie = perfo
pop = classement_population(pop, perfo_copie(:,1))
perfo_copie = perfo
perfo = classement_population(perfo, perfo_copie(:,1)) !subtil :)
!Print de l'état actuel
if (modulo(generation, 100000) == 0) then
print*, "Generation / elite/ moy. ", generation, perfo(1,:), float(sum(perfo(:,1))/taille_pop)
end if
!génocide des nmbre_intrus plus mauvais de la pop et remplacement par des aléatoires
if(nbre_intrus > 0) then
do i = taille_pop-nbre_intrus+1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
end if
!Mariages
pop = pop_mariee (pop, phi, pc, perfo)
!mutations
pop = pop_mutee (pop, pm)
end do !fin boucle sur les generations
pop_storage(loc2x2x3+1, :,:) = pop
print*, "optimal :"
allocate(sequence_notation(3*perfo(1,2)))
call ToString(pop(1,1:perfo(1,2)), sequence_notation)
print*, sequence_notation
print*, "perfo, # moves, entropy"
print*, perfo(1,:), (perfo(1,1)+perfo(1,2))/10
if((perfo(1,1)+perfo(1,2))/10==16) print*, 'This 2x2x3 was solved :)'
print*,''
best2x2x3loc(loc2x2x3+1) = loc2x2x3
best2x2x3elits(loc2x2x3+1, :) = pop(1, :)
best2x2x3fobj(loc2x2x3+1) = -perfo(1, 2) - (16-(perfo(1,1)+perfo(1,2))/10)*100 !pénalise une chiée si 2x2x3 pas solved
deallocate(sequence_notation)
end do !FIN BOUCLE SUR loc2x2x3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Getting into 2-gen !!!!!!!!!!!!!!!!!!!!!!!!!!!
!Classement des élites en fonction de leur fobj définie ci-dessus
200 print*, ''
best2x2x3fobj_copie = best2x2x3fobj
best2x2x3elits = classement_population(best2x2x3elits, best2x2x3fobj_copie)
best2x2x3fobj_copie = best2x2x3fobj
best_temp(:,1) = best2x2x3loc
best_temp(:,2) = best2x2x3fobj
best_temp = classement_population(best_temp, best2x2x3fobj_copie)
best2x2x3loc = best_temp(:,1)
!si on n'a résolu aucun 2x2x3, ou un en plus de 20 moves on doit recommencer !!
if(best_temp(1,2) < -20) then
print*, 'Adding generations to solve the 2x2x3: no good solution was found.'
is_continuing = 1
GOTO 101
end if
!sinon, c'est cool, on continue
deallocate(pop, perfo, perfo_copie)
print*, 'Best 2x2x3 locations : '
print*, best2x2x3loc
print*, ''
print*, 'End of the 2x2x3 search.'
print*, 'Alg will now try to go into 2-gen for the best location.'
print*, 'Location searched : ', best2x2x3loc(1)
print*,''
!On réinitialise cube_scrambled avec scramble, puis setup-rotations, puis best 2x2x3 solution
loc2x2x3 = best2x2x3loc(1)
cube_scrambled = cube_scrambled_copy
do ll=1, loc2x2x3/3
cube_scrambled = rotate_whole_cube(cube_scrambled)
print*, "whole cube clockwise rotation around Oz (= y)"
write(10, *), "whole cube clockwise rotation around Oz (= y)"
end do
select case (modulo(loc2x2x3,3))
case(1)
cube_scrambled = twist_cube(cube_scrambled)
print*, "whole cube rotation around FUR corner (=xy)"
write(10, *), "whole cube rotation around FUR corner (=xy)"
case(2)
do i = 1,2
cube_scrambled = rotate_whole_cube_x(cube_scrambled)
print*, "whole cube rotation around LR horizontal axis (=x)"
write(10, *), "whole cube rotation around LR horizontal axis (=x)"
end do
end select
best_temp(1,:) = fobj(cube_scrambled, cube_solved, best2x2x3elits(1,:), long_code, 3)
cube_scrambled = do_sequence(cube_scrambled, best2x2x3elits(1,1:best_temp(1,2)), best_temp(1,2))
moves_total = best_temp(1,2) !comptage pour le nombre de moves de la solution finale
!Print dans le fichier de résultats
allocate(sol1(3*moves_total))
call ToString(best2x2x3elits(1,1:best_temp(1,2)), sol1)
!!!!!!!!Algo génétique pour la phase 'getting into 2-gen'
taille_pop = 51 !# individus
nbre_intrus = 5 !#individus aléatoires remplacent les plus mauvais de pop à chaque gén.
long_code = 30 !nombre de gènes
Ttot = 10000 !nombre de générations
pm = 0.05 !proba mutation
pc = 0.8 !proba croisement
phi = 1.5 !poids élite
allocate(pop(taille_pop, long_code))
allocate(perfo(taille_pop, 2))
allocate(perfo_copie(taille_pop,2))
if(is2gen(cube_scrambled)) Ttot = 10
!génération population initiale
do i = 1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
201 do generation = 1, Ttot
if(two_gen) pop = mod(pop,2) + 6*(pop/6) !2-gen-> séquence contient uniquement (F,U)
!Raccourci des mouvements qui s'annulent
do i = 1, taille_pop
pop(i,:) = trim_sequence(pop(i,:))
end do
!Evaluation de la fonction objectif sur la population
!pour fobj : 3=2x2x3, 4=is2gen, 2=entropy
do i = 1, taille_pop
perfo(i, :) = fobj(cube_scrambled, cube_solved, pop(i, :), long_code, 4)
!print*, pop(i,:), perfo(i, 1)
end do
!classement de la population
perfo_copie = perfo
pop = classement_population(pop, perfo_copie(:,1))
perfo_copie = perfo
perfo = classement_population(perfo, perfo_copie(:,1)) !subtil :)
!Print de l'état actuel
if (modulo(generation, 5000) == 0) then
print*, "Generation / elite/ moy. ", generation, perfo(1,:), float(sum(perfo(:,1))/taille_pop)
end if
!génocide des nmbre_intrus plus mauvais de la pop et remplacement par des aléatoires
if(nbre_intrus > 0) then
do i = taille_pop-nbre_intrus+1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
end if
!Mariages
pop = pop_mariee (pop, phi, pc, perfo)
!mutations
pop = pop_mutee (pop, pm)
end do !fin boucle sur les generations
!Etait-on déjà dans le 2-gen groupe ?
if(perfo(1,2)==1 .and. is2gen(cube_scrambled)==1) print*, 'Cube was already in the 2-gen group.'
!A-t-on vraiment fini cette étape ?
if( sum(perfo(1,:)) == 1700) then
print*, 'Getting into 2-gen phase is done :)'
allocate(sequence_notation(3*perfo(1,2)))
call ToString(pop(1,1:perfo(1,2)), sequence_notation)
!Mise à jour de l'état de cube_scrambled si on n'y était pas déjà
if(perfo(1,2) > 1) then
cube_scrambled = do_sequence(cube_scrambled, pop(1,1:perfo(1,2)), perfo(1,2))
moves_total = moves_total + perfo(1,2) !comptage pour #moves solution finale
print*, "optimal :"
print*, sequence_notation
print*, "# moves"
print*, perfo(1,2)
end if
else
print*, 'Adding 10''000 gen. to solve this part'
GOTO 201
end if
print*,''
!Prépare le print final dans le fichier
allocate(sol2(size(sequence_notation)))
sol2 = sequence_notation
deallocate(pop, perfo, perfo_copie, sequence_notation)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Solving 2-gen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(twogen_bourrin > 0) then
print*, "calling bourrin"
call solve_twogen_bourrin(cube_scrambled, cube_solved)
GOTO 9999
end if
300 taille_pop = 51 !# individus
nbre_intrus = 5 !#individus aléatoires remplacent les plus mauvais de pop à chaque gén.
long_code = 30 !nombre de gènes
Ttot = 200000 !nombre de générations
pm = 0.05 !proba mutation
pc = 0.8 !proba croisement
phi = 1.5 !poids élite
allocate(pop(taille_pop, long_code))
allocate(perfo(taille_pop, 2))
allocate(perfo_copie(taille_pop,2))
two_gen = 1
!génération population initiale
do i = 1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
301 do generation = 1, Ttot
if(two_gen) pop = mod(pop,2) + 6*(pop/6) !2-gen-> séquence contient uniquement (F,U)
!Raccourci des mouvements qui s'annulent
do i = 1, taille_pop
pop(i,:) = trim_sequence(pop(i,:))
end do
!Evaluation de la fonction objectif sur la population
!pour fobj : 3=2x2x3, 4=is2gen, 2=entropy
do i = 1, taille_pop
perfo(i, :) = fobj(cube_scrambled, cube_solved, pop(i, :), long_code, 2)
!print*, pop(i,:), perfo(i, 1)
end do
!classement de la population
perfo_copie = perfo
pop = classement_population(pop, perfo_copie(:,1))
perfo_copie = perfo
perfo = classement_population(perfo, perfo_copie(:,1)) !subtil :)
!Print de l'état actuel
if (modulo(generation, 10000) == 0) then
print*, "Generation / elite/ moy. ", generation, perfo(1,:), float(sum(perfo(:,1))/taille_pop)
end if
!génocide des nmbre_intrus plus mauvais de la pop et remplacement par des aléatoires
if(nbre_intrus > 0) then
do i = taille_pop-nbre_intrus+1, taille_pop
do j = 1, long_code
call random(r)
pop(i,j) = r*0.9999*18
end do
end do
end if
!Mariages
pop = pop_mariee (pop, phi, pc, perfo)
!mutations
pop = pop_mutee (pop, pm)
end do !fin boucle sur les generations
!Si on a fini, on a fini, sinon on continue ... quel poète ce cyril !
if( (perfo(1,1)+perfo(1,2))/10. == 48) then
print*, "optimal :"
allocate(sequence_notation(3*perfo(1,2)))
call ToString(pop(1,1:perfo(1,2)), sequence_notation)
print*, sequence_notation
print*, "fobj / # moves / entropy"
print*, perfo(1,:), sum(perfo(1,:))/10
moves_total = moves_total + perfo(1,2)
print*, 'Whole cube was solved in ', moves_total, ' moves :)'
else
print*, 'Adding 200 000 more generations to solve this stage.'
GOTO 301
end if
print*,''
!Prépare le print dans le fichier de résultat
allocate(sol3(size(sequence_notation)))
sol3 = sequence_notation
deallocate(pop, perfo, perfo_copie, sequence_notation)
!Print des résultats
999 write(10, *) 'Solution : '
write(10, *), sol1
write(10, *), sol2
write(10, *), sol3
write(10, *), 'total moves # : ', moves_total
close(unit=10)
Deallocate(scramble_sequence, scramble_notation, sol1, sol2, sol3)
9999 print*, 'End of the program.'
read*,i
end program RUBIK
Comments
0 B
|👍
/👎
0 B
|👍
/👎