! 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