php/rubik.php
<?php
/*
6 faces, 54 coloured squares
12 edges, 27 small cubes "cubies"
the 6 faces of the cube are numbered the 9 squares/cublets in each face are
from 0 to 5 numberd counter clock wise
3 white 6 5 4
4 green 0 red 2 blue 5 orange 7 8 3
1 yellow 0 1 2
33 32 31
34 35 30
27 28 29
42 41 40 06 05 04 24 23 22 51 50 49
43 44 39 07 08 03 25 26 21 52 53 48
36 37 38 00 01 02 18 19 20 45 46 47
15 14 13
16 17 12
09 10 11
the address of a square is side * 9 + squareNo from 0 to 53
012 234 456 670
red 0 0 y 654 b 076 w 210 g 432
yellow 1 9 o 210 b 210 r 210 g 210
blue 2 18 y 432 o 076 w 432 r 432
white 3 27 r 654 b 654 o 654 g 654
green 4 36 y 076 r 076 w 076 o 432
orange 5 45 y 210 g 076 w 654 b 432
times
tCells/5 solved 164 rots, 1.06e+8 = 105685137 tries, 195 secs at 2025-02-23T13:30:12
tCells/7 solved 172 rots, 1.45e+8 = 145101655 tries, 228 secs at 2025-02-23T13:38:07
tCells/6 solved 84 rots, 5.63e+7 = 56347636 tries, 102 secs, 65.937 back, 35.75 search, at 2025-02-23T14:39:38
tCells/6.5 solved 84 rots, 4.72e+7 = 47174445 tries, 97 secs, 55.549 back, 40.512 search, at 2025-02-23T14:46:58
tCells/6.8 solved 84 rots, 3.86e+7 = 38552163 tries, 82 secs, 39.822 back, 41.499 search, at 2025-02-23T14:58:24
tCells/7 solved 84 rots, 3.13e+7 = 31269090 tries, 63 secs, 21.544 back, 41.938 search, at 2025-02-23T14:43:12
tCells/7.2 solved 84 rots, 3.13e+7 = 31269090 tries, 63 secs, 21.58 back, 41.379 search, at 2025-02-23T14:49:44
tCells/7.2 solved 84 rots, 3.13e+7 = 31269090 tries, 64 secs, 21.461 back, 41.356 search, at 2025-02-23T14:53:49
tCells/7.4 solved 84 rots, 3.06e+7 = 30619055 tries, 64 secs, 20.794 back, 42.952 search, at 2025-02-23T14:55:50
*/
require_once 'env.php';
class Rubik {
const COL = ['red', 'yellow', 'blue', 'white', 'green', 'orange']
, COW = ['black', 'black', 'white', 'black', 'white', 'black']
, ROT = ['?', '+', '++', '-', '?']
, NR = [1 => [0=>3, 1=>0, 3=>5, 5=>1]
,2 => [0=>4, 2=>0, 4=>5, 5=>2]
]
, ROT8 = ['1' => '3', '3' => '5', '5' => '7', '7' => '1', 'f' => 'f']
, MEMMAX = 3000000000
, SRCH = 'searcG' # the search function/method
# compression decompression
, COMP = 'hex2bin' # 'Rubik::compNo' # 'Rubik::comp19'
, DECO = 'bin2hex' #'Rubik::compNo' #'Rubik::deco19';
;
public static function compNo($c) { # compresses the back table keys to 27Bytes and makes it faster
return $c;
}
public static function compHex($c) { # compresses the back table keys to 27Bytes and makes it faster
return hex2bin($c);
}
public static function decoHex($c) {
return bin2hex($c);
}
public static function comp19($c) { # compresses the back table keys to 19Bytes and makes it slower - the cost of compression is too high
# we have 6 colors and the wildcard which makes for each face 7**8 * 5 possibilites which needs 25bits
# compress that into 3 bytes plus 1 overflow bit
$k = '';
$o = 0;
for ($f=0; $f<6; $f++) {
$f7 = strtr($c[$f*9+8], '1357f', '01234') . strtr(substr($c, $f*9, 8), 'f', '6');
$i = intval($f7, 7);
$o |= ($i & (1<<24)) >> (19+$f);
# dbq("COMP i $i", dechex($i) , ", o $o");
$k .= chr(($i >>16) & 255) . chr(($i >>8) & 255) . chr($i & 255);
}
# out("comp19 $c =>", bin2hex($r=$k . chr($o)), "=>", $q = self::deco19($r), $c === $q);
#if($c !== self::deco19($k . chr($o)))
# err("comp19 $c =>", bin2hex($r=$k . chr($o)), "=>", $q = self::deco19($r), $c === $q);
return $k . chr($o);
}
public static function deco19($k) {
$o = ord($k[18]);
# dbq("o $o, oD $oD");
$c = '';
for ($f=0; $f<6; $f++) {
$i = (($o << (19+$f)) & (1 << 24)) | (ord($k[$f3=$f*3])<<16) | (ord($k[$f3+1])<<8) | ord($k[$f3+2]);
$j = str_pad(base_convert((string) $i, 10, 7), 9, '0', STR_PAD_LEFT);
# dbq("i $i =>j $j");
$c .= strtr(substr($j, 1, 8), '6', 'f') . strtr($j[0], '01234', '1357f');
}
return $c;
}
static $fRot, $rRot, $r2n, $rTrg, $r2tr, $tgt, $vis, $seq, $rC
, $depthMax = 55
, $backMax = 5;
public static function linIO($t, $c) {
if (OutHtml)
self::linI($t, $c);
elseif ($c !== '')
self::linO($t, $c);
}
public static function config () {
$c2r = []; # cartesian coordinate (x,y,z) <=> 100z + 10y +x, each 0,1,2 (yielding the 3**3 small cubes)
# to Rubrik Coordinates = face= * 9 + colourCell=smallSquareNo => 54 small squares + 10*y + >of the small cube (the center square changes orientation, not color)
foreach ([[0, 1, 10], [200, 1, -100], [2, 100, 10], [20,1,100], [200, -100,10 ], [202, -1, 10]] as $f => $t) {
$c2r[$a = $t[0]][] = $f*9;
foreach ([1, 1, 2, 2, 5, 5, 6, 1] as $c => $v)
$c2r[($v & 4) ? $a -= $t[$v & 3] : $a += $t[$v & 3]][] = $f * 9 + $c + 1;
}
#dbq('config c2r', count($c2r), $c2r);
$r2n = []; # Rubik Coordinates (9*f+c) => neighbours over the edges
foreach ($c2r as $n) {
if (count($n) !== 2)
continue;
$r2n[$n[0]] = $n; # c in middle of the edge => [c, neighbour over the edge]
$r2n[$n[1]] = [$n[1], $n[0]];
}
foreach ($c2r as $n) {
if (count($n) !== 3)
continue;
foreach ($n as $x => $c) { # c a corner => [c, neighbour on face of neighbour of predessor, neighbour on face of neighbour of successor]
$b = $r2n[($c % 9 === 0) ? $c+7 : $c-1][1]; # neighbour of predecessor
$r2n[$c] = intdiv($n1 = $n[($x+1) % 3], 9) === intdiv($b, 9) ? [$c, $n1, $n[($x+2) % 3]] : [$c, $n[($x+2) % 3], $n1];
}
}
ksort($r2n);
self::$r2n = $old = $r2n;
/*--- r2n gives the neighours of each Rubrik ColourCell
edge e => [e, f] with f the cell over the edge e.g. 1 => [1, 14]
vertex v => [v, w, x] with w the neigbour on the face over the edge before v and x the neigbour on the face over the edge after v, e.g. 2 => [2, 13, 18]
--- */
$r2n = [1 => [1, 14], 3 => [3, 25], 5 => [5, 28], 7 => [7, 39], 21 => [21, 52]]; # glue the the six faces at 5 edges
do {
$lC = count($r2n);
# dbq("----while $lC", $r2n);
for ($f=0; $f<54; $f+=9) {
for ($c=1; $c<8; $c+=2) {
if (! ($g = $r2n[$f + $c] ?? false)) # current edge g not in r2n yet
continue;
$gR = array_reverse($g);
if ($gR !== $r2n[$g[1]] ??= $gR)
err("bad edge", $r2n[$g[1]], 'reverse', $gR);
if (! ($h = ($r2n[$f + ($c+2) % 8] ?? false))) # next edge h not yet defined
continue;
# v is the vertex between g and h, the orientation of neighbouring faces run in opposite direction along the connecting edge
$v = [$f + ($c+1) % 8, $g[1] - 1, $h[1] + (($h[1] % 9 < 7) ? +1 : -7)];
if ($v !== ($r2n[$v[0]] ??= $v))
err("bad vertex", $r2n[$v[0]], ' differs ', $v, $r2n);
([$v[1], $v[2], $v[0]] === ($r2n[$v[1]] ??= [$v[1], $v[2], $v[0]])) or err("mismatch vertex 1", $v);
([$v[2], $v[0], $v[1]] === ($r2n[$v[2]] ??= [$v[2], $v[0], $v[1]])) or err("mismatch vertex 2", $v);
$i = [$g[1] + (($g[1] % 9 >= 2) ? -2 : +6 ), $h[1] + (($h[1] % 9 < 6) ? +2 : -6 )]; # i is neighbourhood of third edge of vertex
if ($i !== ($r2n[$i[0]] ??= $i))
err("third edge", $i, 'mismatches', $r2n[$i[0]]);
}
}
} while ($lC !== count($r2n));
ksort($r2n);
self::$r2n = $old = $r2n;
$r2n === $old or err("config r2n", $r2n === $old, "\nnew", count($r2n), $r2n, "\nold", count($old), $old);
$cd = '';
for ($f=0;$f<6;$f++) { # fRot: rotate one face
$g = $f*9;
$t2f = [$g+8=>'r2'];
for ($t=0;$t<8;$t++)
$t2f[$g+$t] = $g + ($t + 6) % 8;
for ($t=0;$t<8;$t+=2) {
$t2f[$r2n[($t+2) % 8 + $g][2]] = $r2n[$t + $g][2];
$t2f[$r2n[($t+3) % 8 + $g][1]] = $r2n[$t+1 + $g][1];
$t2f[$r2n[($t+4) % 8 + $g][1]] = $r2n[($t+2) % 8 + $g][1];
}
$cd .= "\n , $f => " . self::mkCode($t2f);
}
$cd = "[" . substr($cd, 6) . "\n ];";
# dbq("fRot $cd");
self::$fRot = eval("return $cd");
$tt = []; # rRot: rotate the whole Rubik, around this face
for ($f=1;$f<3;$f++) {
$g = $f*9;
$t2f = [];
for ($k=0;$k<8;$k+=2) {
$t2f[$k + $g] = ($k+6) % 8 + $g; # current face
$t2f[$k + 1 + $g] = ($k+7) % 8 + $g;
$fG = ($fA = $r2n[($k+6) % 8 + $g][2]) - ($fC = $fA % 9); # neighbour face
$fD = (($fQ = $r2n[($k+7) % 8 + $g][1]) - $fA + 8) % 8;
(intdiv($fQ, 9) * 9 === $fG) or err("fA $fA not in same face as fQ $fQ");
$tG = ($tA = $r2n[$k + $g][2]) - ($tC = $tA % 9);
$tD = (($tQ = $r2n[$k+ 1 + $g][1]) - $tA + 8) % 8;
(intdiv($tQ, 9) * 9 === $tG) or err("tA $tA not in same face as tQ $tQ");
for ($j=0; $j<8; $j++)
$t2f[($tC + $j * $tD) % 8 + $tG] = ($fC + $j * $fD) % 8 + $fG;
$t2f[8 + $tG] = 8 + $fG;
$t2f[$tM = $r2n[($tC + 5 * $tD) % 8 + $tG][1]] = $r2n[($fC + 5 * $fD) % 8 + $fG][1]; # turn oposite face
$t2f[$tN = $r2n[($tC + 6 * $tD) % 8 + $tG][2]] = $r2n[($fC + 6 * $fD) % 8 + $fG][2];
(intdiv($tM, 9) === intdiv($tN, 9)) or err("tM $tM not in same face as tN $tN");
}
$tt[intdiv($t2f[0] ?? 0, 9)] = $t2f;
$tt[intdiv(($fl = array_flip($t2f))[0] ?? 0, 9)] = $fl;
}
$t2f = [];
foreach ($ff = $tt[2] as $t => $f)
$t2f[$t] = $ff[$f];
$tt[intdiv($t2f[0] ?? 0, 9)] = $t2f;
ksort($tt);
# dbq("rRot tt", array_keys($tt), $tt);
$cd = "0 => " . self::mkCode([]);
foreach($tt as $f => $t2f)
$cd .= "\n , $f => " . self::mkCode($t2f);
$cd = "[ $cd\n ]";
# dbq("rRot $cd");
self::$rRot = eval("return $cd;");
# rTrg target with center cells
$t = [];
for ($c = 0; $c < 8; $c++)
$t[] = $r2n[$c];
for ($c = 1; $c < 8; $c+=2)
$t[] = $r2n[($a = $r2n[$c][1]) + ($a % 9 < 6 ? 2 : -6)];
for ($c = 0; $c < 8; $c++)
$t[] = $r2n[(10 - $c) % 8 + 45];
self::$rTrg = array_map(fn ($v) => $r2n[min(...$v)], $t); # smallest index first
self::$r2tr = [];
foreach (self::$rTrg as $i => $n) {
self::$r2tr[$n[0]] = $i;
self::$r2tr[$n[1]] = $i;
if(isset($n [2]))
self::$r2tr[$n[2]] = $i;
}
# dbq('rTrg', self::$rTrg);
# dbq('r2tr', self::$r2tr);
} # end config
public static function mkCode($t2f) {
$t2f[54] = 54;
ksort($t2f);
$cO = 0;
$fF = $l = -1;
$fl = [];
foreach ($t2f as $t => $f) {
if ($f === $fF + $l) {
$l++;
continue;
}
if ($fF >= 0) {
$fl[] = [$fF, $l];
$cO += $l;
}
if ($cO < $t) {
$fl[] = [$cO, $t - $cO];
$cO = $t;
} elseif ($cO > $t) {
err("cO $cO > tF $t");
}
if (['r2' => 2, 'r4' => 4, 'r6' => 6][$f] ?? false) {
$fl[] = [$t, $f];
$cO++;
$fF = $l = -1;
} else {
$fF = $f;
$l = 1;
}
}
# dbQ("r", count($fl), $fl);
$cL = 18;
$cd = '';
$cO = 0;
foreach ($fl as list($f, $l)) {
if ($cO >= $cL) {
$cd .= " # $cO\n ";
$cL = (intdiv($cO, 18) + 1) * 18;
}
if (is_int($l)) {
$cd .= ($l > 1) ? " . substr(\$c, $f, $l)" : ($l === 1 ? " . \$c[$f]" : err("l < 1 [$f, $l]"));
$cO += $l;
} elseif ($l === 'r2') { # }'$lx = ['r2' => 2, 'r4' => 4, 'r6' => 6][$l] ?? err("bad l $l")) {
$cd .= " . (self::ROT8[\$c[$f]] ?? err(\"bad ROT8 c[$f] \$c[$f]\"))";
$cO++;
} else {
err("bad fl [$f, $l]");
}
}
$cd = "function (\$c) { return\n " . substr($cd, 2) . "; }";
# dbq("cd $cd");
return $cd;
} # end mkCode
/*
foreach ($tE as $k0 => $v0) {
foreach ($tE as $k1 => $v1) {
foreach ($tE as $k2 => $v2) {
$v = ($k0 * 7) + $k1) *
}
}
}
*/
public static function test($n) {
$d = $c = '000000001111111111222222221333333331444444441555555551';
static $rr = # runtime secs for searcE searcF ($) (const) (call) comp19
[ 0 => '013524405310355' # 63 31 33 31 32 63
, 1 => '0135244053' # 147 72 73
, 2 => '01352440531035513445200022455533122' # needs 22 minutes (target 38!)
, 3 => '000444331114445552223330555345511323551510011001115503444220'
];
$nL = intdiv($n, 10);
if (9 !== $nM = $n % 10) {
$rrN = $nL === 0 ? $rr[$n] : str_pad('', $nL, $rr[$nM]);
} else {
$rrN = str_repeat($lC = intdiv($r = random_int(0, 17), 3), 1 + $r % 3);
for($l = $nL ? $nL : 30; strlen($rrN) < $l;) {
if ($lC === $aC = intdiv($r = random_int(0, 14), 3))
$aC = 5;
$rrN .= str_repeat($lC = $aC, 1 + $r % 3);
}
$rrN = substr($rrN, 0, $l);
}
for ($i=0; $i < strlen($rrN); $i++)
$d = self::$fRot[(int) $rrN[$i]]($d);
out("test search", self::SRCH, ", compress", SELF::COMP, ", testcase $n with", strlen($rrN), $rrN, 'rots testCase', $d);
return $d;
}
public static function search($c, $d, $t) {
self::$tgt = $t;
# dbq("tgt", self::$tgt);
self::$vis = [$c => -1];
self::$rC = 0;
return self::searc1($c, $d);
}
public static function searc1($c, $d) {
for ($r = 0; $r < 6; $r++) {
$n = self::$fRot[$r]($c);
self::$rC ++;
# dbq("s" . self::$rC . "rot $r, $c ==> $n");
if (isset(self::$vis[$n]))
continue;
$eq = true;
foreach (self::$tgt as $t => $v)
if ($n[$t] !== $v) {
$eq = false;
break;
}
self::$vis[$n] = $r;
if ($eq)
return self::$vis;
if ($d>1 and $s = self::searc1($n, $d-1))
return $s;
(isset(self::$vis[$n])) or err("vis n lost");
(array_pop(self::$vis) === $r) or err("mismatch pop");
(isset(self::$vis[$n])) and err("pop did not unset n");
}
}
public static function searcC($c, $d, $t) {
self::$tgt = $t;
self::$vis = [$c => -1];
self::$rC = 0;
return self::searc2($c, $d, 999);
}
public static function searc2($c, $d, $nR) {
for ($r = 0; ($r === $nR) and $r++, $r < 6; $r++) {
$n = $c;
for ($r2=1; $r2<4; $r2++) {
$n = self::$fRot[$r]($n);
self::$rC ++;
# dbq("s" . self::$rC . "rot $r, $c ==> $n");
if (isset(self::$vis[$n]))
continue;
$eq = true;
foreach (self::$tgt as $t => $v)
if ($n[$t] !== $v) {
$eq = false;
break;
}
self::$vis[$n] = $r*10+$r2;
if ($eq)
return self::$vis;
if ($d>1 and $s = self::searc2($n, $d-1, $r))
return $s;
(isset(self::$vis[$n])) or err("vis n lost");
(array_pop(self::$vis) === $r*10+$r2) or err("mismatch pop");
(isset(self::$vis[$n])) and err("pop did not unset n");
}
}
}
static function trgNxt($tt, $ix, $c) {
foreach (self::$rTrg[$ix] as $t2)
$tt[$t2] = (string) intdiv($t2, 9);
$u = $done = [];
$tK = 0;
$u = $done = [];
$tK = 0;
foreach ($tt as $tR => $tC) {
if ($c[$tR] !== $tC) {
$tK++;
if (! isset($done[$tR])) {
foreach ($tN = self::$r2n[$tR] as $n)
$done[$n] = 1;
$u[] = self::$r2n[min(...$tN)];
}
}
}
$m = "target " . count($tt) . " colourCells, $tK changes";
foreach ($u as $v) {
$tCC = (1 << $tt[$v[0]]) | (1 << $tt[$v[1]]) | (isset($v[2]) ? (1 << $tt[$v[2]]) : 0);
$m .= ", [$v[0] => " . $tt[$v[0]] . ", $v[1] => " . $tt[$v[1]] . (isset($v[2]) ? (", $v[2] => " . $tt[$v[2]]) : '') . "] $tCC";
$cCC = 0;
foreach(self::$r2n as $n)
if ($tCC === $cCC = (1 << $c[$n[0]]) | (1 << $c[$n[1]]) | (isset($n[2]) ? (1 << $c[$n[2]]) : 0))
break;
($tCC === $cCC) or err("tCC !== cCC");
$n = self::$r2n[min(...$n)];
$m .= " <--< [$n[0] => " . $c[$n[0]] . ", $n[1] => " . $c[$n[1]] . (isset($n[2]) ? (", $n[2] => " . $c[$n[2]]) : '') . "]";
}
out("diff $m");
return $tt;
}
static function solve($tst, $o, $stx=2) {
$c = Rubik::test($tst);
$o->slvB(self::SRCH . " stx=$stx");
$rotCC = 0;
while (($r = new Rubik($c, $stx))->tTo > $r->tFr) {
$r->rotL = $o->rotLTot - $rotCC;
$o->srchB($r);
$tB = time();
$nn = $r->{Rubik::SRCH}();
$tE = time();
$o->srchE($nn);
if (! is_array($nn))
break;
$d = $c;
$o->rot('from', $c);
foreach($nn as $n) {
for ($k = $n % 10; $k > 0; $k--)
$d = Rubik::$fRot[intdiv($n, 10) ]($d);
$o->rot($n, $d);
}
$o->rotE();
$rotCC += $o->rotCC;
$c = $d;
}
$o->slvE($c);
}
public $fA, $tA, $rotC, $rotL, $back, $tBack = 0, $tSrch = 0;
public function __construct(public $from, $tP) { # construct an instance for Rubiik colours $from and target rTrg[0...$ty]
$this->rotL = (int) 1e8;
$t = $f = str_repeat('f', 54); # start with from to all unknown
$mm = '';
$tc = -1;
$ty = count(self::$rTrg) - 1;
$this->tFr = 99;
$this->tFc = 99;
foreach (self::$rTrg as $tx => $n) {
$h2 = isset($n[2]);
$tc += 2 + (int) $h2;
$t[$n[0]] = $c0 = (string) intdiv($n[0], 9); # set the 2 or 3 t colours
$t[$n[1]] = $c1 = (string) intdiv($n[1], 9);
$h2 and ($t[$n[2]] = $c2 = (string) intdiv($n[2], 9));
if ($from[$n[0]] === $c0 and $from[$n[1]] === $c1 and ((! $h2) or $from[$n[2]] === $c2)) { # from ist already correct, colour from
$f[$n[0]] = $c0;
$f[$n[1]] = $c1;
$h2 and ($f[$n[2]] = $c2);
} else {
if ($this->tFr > $tx) {
$this->tFr = $tx - 1;
$this->tFc = $tc - 2 - (int) $h2;
$ty = min($ty , $tx+$tP - 1);
}
$tz ??= $tx; # from has other colours
$tCC = (1 << $c0) | (1 << $c1) | ($h2 ? (1 << $c2) : 0); # search the needed cubelet in from
foreach(self::$r2n as $q)
if ($tCC === $fCC = (1 << $from[$q[0]]) | (1 << $from[$q[1]]) | (isset($q[2]) ? (1 << $from[$q[2]]) : 0))
break; # this is the needed cubelt
($tCC === $fCC) or err("tCC $tCC !== fCC $fCC c $c0 $c1 $c2", $n);
$f[$q[0]] = $from[$q[0]]; # colour needed cubelet in from
$f[$q[1]] = $from[$q[1]];
$h2 and ($f[$q[2]] = $from[$q[2]]);
$q = self::$r2n[min(...$q)];
$mm .= ", [$n[0]=$c0,$n[1]=$c1" . ($h2 ? ",$n[2]=$c2" : '') . "] << [$q[0]="
. $from[$q[0]] . ",$q[1]=" . $from[$q[1]] . ($h2 ? (",$q[2]=" . $from[$q[2]]) : '') . "]";
}
if ($tx >= $ty)
break;
}
$this->tTo = $tx;
$this->tTc = $tc;
$this->tA = $t;
$this->fA = $f;
$this->cm1 = "from $this->tFr/$this->tFc to target $this->tTo/$this->tTc cublets/colors";
$this->cm2 = "changes" . substr($mm, 1);
#dbq("\nfrom = $from\nfA = $f\ntA = $t");
}
public function searcD() {
$bk = [$this->tA => 90];
$bkC = max(2, min(self::$backMax, round($this->tTc / 7.2)));
$this->rotC = 0;
$this->tBack = -hrtime(true);
$mL = $m0 = time() + 3;
$tB = hrtime(true);
$tRC = $this->rotC;
$tBK = count($bk);
for ($bx=0; $bx<$bkC; $bx++) {
$bz = 100 + $by = $bx * 100;
foreach($bk as $c => $d) {
if ($d < $by or $d > $bz)
continue;
$rNo = intdiv($d, 10) % 10;
for ($r = 0; ($r === $rNo) and $r++, $r < 6; $r++) {
for ($c2=$c, $r2=1; $r2<4; $r2++) {
$c2 = self::$fRot[$r]($c2);
$this->rotC++;
$bk[$c2] ??= $bz + 10*$r + $r2;
}
}
if ($mL < time()) {
if (memory_get_usage(true) > self::MEMMAX) {
out(sprintf("back: memLimit %.2e exceeded by usage %.2e, no longer expanding back", self::MEMMAX, memory_get_usage(true)));
break;
}
$mL +=2;
$tN = hrtime(true);
echo "*** rotC " . round(($this->rotC - $tRC) / ($tN-$tB) * 1e9) . ", bk " . round((count($bk) - $tBK) / ($tN-$tB) * 1e9) . sprintf(' memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)) . date(' Y-m-d\TH:i:s') . " \r";
$tB = $tN;
$tRC = $this->rotC;
$tBK = count($bk);
}
}
$tN = hrtime(true);
if ($mL > $m0)
echo "\n... rotC " . round(($this->rotC - $tRC) / ($tN-$tB) * 1e9) . ", bk " . round((count($bk) - $tBK) / ($tN-$tB) * 1e9) . sprintf(' memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)) . date(' Y-m-d\TH:i:s') . " \n";
dbg1("bx $bx, rotC $this->rotC, bk", count($bk), sprintf('memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)), date('Y-m-d\TH:i:s'));
if (isset($bk[$this->fA]))
break;
}
$this->tBack += hrtime(true);
if (isset($bk[$this->fA])) {
$stps = [$this->fA];
} else {
$this->back = $bk;
$this->tSrch -= hrtime(true);
for ($dx=1; $dx < self::$depthMax - $bkC; $dx++) {
$stps = $this->searcDR($this->fA, $dx, 9);
out("searcDR $dx,", sprintf('%7.2e =', $this->rotC), $this->rotC, "tries,", date('Y-m-d\TH:i:s'));
if (! is_null($stps))
break;
}
$this->tSrch += hrtime(true);
}
# dbq('stps 1', $stps);
if (is_null($stps))
return null;
$stps = array_reverse($stps);
$g = array_pop($stps);
# dbq('stps 2', $stps);
# dbq("to back $g");
while (90 !== $r = ($bk[$g] ?? err("missing in back $g")) % 100) {
$stps[] = $r += 4 - ($r % 10) * 2;
for ($k = $r % 10; $k > 0; $k--)
$g = self::$fRot[intdiv($r, 10)]($g);
# dbq("back $r > $g");
}
dbq('return stps', count($stps), $stps);
return $stps;
} # end seachD
public function searcE() {
$bk = [$this->tA => 90];
$bx = $sx = 0;
$bkC = max(2, min(self::$backMax, round($this->tTc / 7.2)));
$bMore = true;
$bTi = $this->tBack = $sTi = $this->tSrch = 0;
$this->rotC = 0;
while (true) {
if ($bx + $sx >= self::$depthMax)
return null;
$tL = 3e9 + $tB = hrtime(true);
if ($bx < $bkC or ($bMore and $bTi < $sTi * 0.5)) { # expand back
$laBa = true;
$bz = 100 + $by = $bx * 100;
$bx++;
foreach($bk as $c => $d) {
if ($d < $by or $d > $bz)
continue;
$rNo = intdiv($d, 10) % 10;
for ($r = 0; ($r === $rNo) and $r++, $r < 6; $r++) {
for ($c2=$c, $r2=1; $r2<4; $r2++) {
$c2 = self::$fRot[$r]($c2);
$this->rotC++;
$bk[$c2] ??= $bz + 10*$r + $r2;
}
}
if ($tL < hrtime(true)) {
if (memory_get_usage(true) > self::MEMMAX) {
out(sprintf("back: memLimit %.2e exceeded by usage %.2e, no longer expanding back", self::MEMMAX, memory_get_usage(true)));
$bMore = false;
break;
}
$tL = hrtime(true) + 2e9;
}
}
$this->tBack += $bTi = ($tN = hrtime(true)) - $tB;
dbg1("bx $bx, rotC $this->rotC, bk", count($bk), sprintf('memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)), date('Y-m-d\TH:i:s'));
if (isset($bk[$this->fA])) {
$stps = [$this->fA];
break;
}
$this->back = $bk;
} else {
$laBa ? ($laBa = false) : $sx++;
$stps = $this->searcDR($this->fA, $sx, 9);
# dbq("searcDR $sx,", sprintf('%7.2e =', $this->rotC), $this->rotC, "tries,", date('Y-m-d\TH:i:s'));
$this->tSrch += $sTi = hrtime(true) - $tB;
if (! is_null($stps))
break;
}
}
$stps = array_reverse($stps);
$g = array_pop($stps);
# dbq('stps 2', $stps);
# dbq("to back $g");
while (90 !== $r = ($bk[$g] ?? err("missing in back $g")) % 100) {
$stps[] = $r += 4 - ($r % 10) * 2;
for ($k = $r % 10; $k > 0; $k--)
$g = self::$fRot[intdiv($r, 10)]($g);
# dbq("back $r > $g");
}
# dbq('return stps', count($stps), $stps);
return $stps;
} # end seachE
public function searcDR($c, $d, $nR) {
for ($r = 0; $r < 6; $r++) {
if ($r === $nR)
continue;
$n = $c;
for ($r2=1; $r2<4; $r2++) {
$n = self::$fRot[$r]($n);
$this->rotC ++;
# dbq("s" . self::$rC . "rot $r, $c ==> $n");
if (isset($this->back[$n]))
return [$n, $r *10 + $r2];
if ($d>1 and ($s = $this->searcDR($n, $d-1, $r))) {
array_push($s, $r *10 + $r2);
return $s;
}
}
}
}
public function searcF() {
$bk = [(self::COMP)($this->tA) => 90];
$bx = $sx = 0;
$bkC = max(2, min(self::$backMax, round($this->tTc / 7.2)));
$bMore = true;
$bTi = $this->tBack = $sTi = $this->tSrch = 0;
$this->rotC = 0;
while (true) {
if ($bx + $sx >= self::$depthMax)
return null;
$tL = 3e9 + $tB = hrtime(true);
if ($bx < $bkC or ($bMore and $bTi < $sTi * 0.5)) { # expand back
$laBa = true;
$bz = 100 + $by = $bx * 100;
$bx++;
foreach($bk as $c => $d) {
if ($d < $by or $d > $bz)
continue;
$rNo = intdiv($d, 10) % 10;
$c = (self::DECO)($c);
for ($r = 0; ($r === $rNo) and $r++, $r < 6; $r++) {
for ($c2=$c, $r2=1; $r2<4; $r2++) {
$c2 = self::$fRot[$r]($c2);
$this->rotC++;
$bk[(self::COMP)($c2)] ??= $bz + 10*$r + $r2;
}
}
if ($tL < $tN = hrtime(true)) {
if (memory_get_usage(true) > self::MEMMAX) {
out(sprintf("back: memLimit %.2e exceeded by usage %.2e, no longer expanding back", self::MEMMAX, memory_get_usage(true)));
$laBa = $bMore = false;
break;
}
$tL = $tN + 2e9;
}
}
$this->tBack += $bTi = ($tN = hrtime(true)) - $tB;
# dbq("bx $bx, rotC $this->rotC, bk", count($bk), sprintf('memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)), date('Y-m-d\TH:i:s'));
if (isset($bk[(self::COMP)($this->fA)])) {
$stps = [$this->fA];
break;
}
$this->back = $bk;
} else {
$laBa ? ($laBa = false) : $sx++;
$stps = $this->searcFR($this->fA, $sx, 9);
# dbq("searcFR $bx " . count($bk) . "/$sx,", sprintf('%7.2e =', $this->rotC), $this->rotC, "tries,", date('Y-m-d\TH:i:s'));
$this->tSrch += $sTi = hrtime(true) - $tB;
if (! is_null($stps))
break;
}
}
$stps = array_reverse($stps);
$g = array_pop($stps);
# dbq('stps 2', $stps);
# dbq("to back $g");
while (90 !== $r = ($bk[(self::COMP)($g)] ?? err("missing in back $g")) % 100) {
$stps[] = $r += 4 - ($r % 10) * 2;
for ($k = $r % 10; $k > 0; $k--)
$g = self::$fRot[intdiv($r, 10)]($g);
# dbq("back $r > $g");
}
$this->bkI = sprintf('back %d, %7.2e sz, mem %7.2e, r %7.2e', $bx, count($bk), memory_get_usage(), memory_get_usage(true));
#out('searchE', count($stps), 'stps', $stps);
return $stps;
} # end searchE
public function searcFR($c, $d, $nR) {
for ($r = 0; $r < 6; $r++) {
if ($r == $nR)
continue;
$n = $c;
for ($r2=1; $r2<4; $r2++) {
$n = self::$fRot[$r]($n);
$this->rotC ++;
# dbq("s" . self::$rC . "rot $r, $c ==> $n");
if (isset($this->back[(self::COMP)($n)]))
return [$n, $r *10 + $r2];
if ($d>1 and ($s = $this->searcFR($n, $d-1, $r))) {
array_push($s, $r *10 + $r2);
return $s;
}
}
}
}
public function searcG() {
$this->back = [(self::COMP)($this->tA) => 90];
$bx = $sx = 0;
$bkC = max(2, min(self::$backMax, round($this->tTc / 7.2)));
$bMore = true;
$bTi = $this->tBack = $sTi = $this->tSrch = 0;
$this->rotC = 0;
do {
if ($bx + $sx >= self::$depthMax)
return null;
$tB = hrtime(true);
if ($bx < $bkC or ($bMore and $bTi < $sTi * 0.5)) { # expand back
$stps = $this->searcGBack(++$bx);
$this->tBack += $bTi = hrtime(true) - $tB;
$laBa = true;
dbg1("bx $bx, rotC $this->rotC, bk", count($this->back), sprintf('memory %7.2e real %7.2e,', memory_get_usage(), memory_get_usage(true)), date('Y-m-d\TH:i:s'));
} else {
$laBa ? ($laBa = false) : $sx++;
$stps = $this->searcGR($this->fA, $sx, 9);
$this->tSrch += $sTi = hrtime(true) - $tB;
}
} while (! $stps);
$this->bkI = sprintf('back %d, %7.2e sz, mem %7.2e, r %7.2e', $bx, count($this->back), memory_get_usage(), memory_get_usage(true));
if (is_string($stps))
return $stps;
$stps = array_reverse($stps);
$g = array_pop($stps);
# dbq('stps 2', $stps);
# dbq("to back $g");
while (90 !== $r = ($this->back[(self::COMP)($g)] ?? err("missing in back $g")) % 100) {
$stps[] = $r += 4 - ($r % 10) * 2;
for ($k = $r % 10; $k > 0; $k--)
$g = self::$fRot[intdiv($r, 10)]($g);
# dbq("back $r > $g");
}
# dbq('searchG', count($stps), 'stps', $stps);
return $stps;
} # end searchG
public function searcGR($c, $d, $nR) {
for ($r = 0; $r < 6; $r++) {
if ($r == $nR)
continue;
if ($this->rotC > $this->rotL)
return "too many tries $this->rotC > $this->rotL";
$n = $c;
for ($r2=1; $r2<4; $r2++) {
$n = self::$fRot[$r]($n);
$this->rotC ++;
# dbq("s" . self::$rC . "rot $r, $c ==> $n");
if (isset($this->back[(self::COMP)($n)]))
return [$n, $r *10 + $r2];
if ($d>1 and ($s = $this->searcGR($n, $d-1, $r))) {
if (is_array($s))
$s[] = $r *10 + $r2;
return $s;
}
}
}
} # end searchGR
public function searcGBack($bx) {
# $tL = 3e9 + $tB = hrtime(true);
# $laBa = true;
$by = -100 + $bz = $bx * 100;
foreach($this->back as $c => $d) {
if ($d < $by)
continue;
if ($d >= $bz)
err("searcGBack bx $bx but d $dx");
$rNo = intdiv($d, 10) % 10;
$c = (self::DECO)($c);
if ($this->rotC > $this->rotL)
return "too many tries $this->rotC > $this->rotL";
for ($r = 0; $r < 6; $r++) {
if ($r === $rNo)
continue;
for ($c2=$c, $r2=1; $r2<4; $r2++) {
$c2 = self::$fRot[$r]($c2);
$this->rotC++;
$this->back[(self::COMP)($c2)] ??= $bz + 10*$r + $r2;
}
}
/* if ($tL < $tN = hrtime(true)) {
if (memory_get_usage(true) > self::MEMMAX) {
out(sprintf("back: memLimit %.2e exceeded by usage %.2e, no longer expanding back", self::MEMMAX, memory_get_usage(true)));
$laBa = $bMore = false;
break;
}
$tL = $tN + 2e9;
}
*/ }
return isset($this->back[(self::COMP)($this->fA)]) ? [$this->fA] : null;
} # end searcGBack
} # end class Rubik
class RubikOutCLI {
static $line;
public static function ini () {
self::$line = # format Rubik human readable
[ 10 => fn($c, $g) => $c[$g+6] . $c[$g+5] . $c[$g+4] # top line of face
, 11 => fn($c, $g) => $c[$g+7] . $c[$g+8] . $c[$g+3] # middle line of face
, 12 => fn($c, $g) => substr($c, $g , 3) # bottom line of face
, 0 => fn($c) => ' ' . self::$line[10]($c, 27) . ' ' # line 0 to 8 of the unwinding of the 6 faces
, 1 => fn($c) => ' ' . self::$line[11]($c, 27) . ' '
, 2 => fn($c) => ' ' . self::$line[12]($c, 27) . ' '
, 3 => fn($c) => ($l = self::$line[10])($c, 36) . ' ' . $l($c, 0) . ' ' . $l($c, 18) . ' ' . $l($c, 45)
, 4 => fn($c) => ($l = self::$line[11])($c, 36) . ' ' . $l($c, 0) . ' ' . $l($c, 18) . ' ' . $l($c, 45)
, 5 => fn($c) => ($l = self::$line[12])($c, 36) . ' ' . $l($c, 0) . ' ' . $l($c, 18) . ' ' . $l($c, 45)
, 6 => fn($c) => ' ' . self::$line[10]($c, 9) . ' '
, 7 => fn($c) => ' ' . self::$line[11]($c, 9) . ' '
, 8 => fn($c) => ' ' . self::$line[12]($c, 9) . ' '
, 20 => fn($c) => self::$line[10]($c, 27) . ' / ' . self::$line[3]($c) . ' / ' . self::$line[10]($c, 9) # line 0 to 2 of the linearization of the 6 faces
, 21 => fn($c) => self::$line[11]($c, 27) . ' / ' . self::$line[4]($c) . ' / ' . self::$line[11]($c, 9)
, 22 => fn($c) => self::$line[12]($c, 27) . ' / ' . self::$line[5]($c) . ' / ' . self::$line[12]($c, 9)
];
}
public function __construct() {
if (! self::$line)
self::ini();
}
public function out($t, $c) { # output text $t and Rubik $c
out('--- ' . (is_int($t) ? "rot $t" : $t) . ' ---');
for ($l=0; $l<9; $l++)
out(self::$line[$l]($c));
}
public function slvB2() {}
public function slvE2() {}
public function rot2($t, $c) { $this->out($t, $c); }
public function rotE2() {}
public function linL($c) {
for ($l=20; $l<23; $l++)
out(self::$line[$l]($c));
}
public function linR($c, $r) {
$d = self::$rRot[$r]($c);
for ($l=0; $l<9; $l++)
out(self::$line[$l]($c) . " -r$r- ". self::$line[$l]($d));
}
} # end class RubikOutCli
class RubikOutHTML {
const JSFUN = <<<JS
<script type="text/javascript">
function rubik(ctx, tlX, tlY, cc) {
const s = 12, w=1;
const face = [[3*s+4*w,3*s+4*w], [3*s+4*w, 6*s+6*w], [6*s+6*w,3*s+4*w],[3*s+4*w, 2*w], [2*w, 3*s+4*w], [9*s+8*w, 3*s+4*w]];
const cell = [[0,2], [1,2], [2,2],[2,1], [2,0], [1, 0], [0,0], [0,1], [1,1]];
const col = ['red', 'yellow', 'blue', 'white', 'green', 'orange'];
const coW = ['black', 'black', 'white', 'black', 'white', 'black'];
const orient = ['?', "\u{2193}", '?', "\u{2192}", '?', "\u{2191}", '?', "\u{2190}", '?'];
ctx.lineWidth = 2*w;
ctx.strokeType = "black";
ctx.strokeRect(tlX+w, tlY+3*s+3*w, 12*s+8*w, 3*s+2*w);
ctx.strokeRect(tlX+3*s+3*w, tlY+w, 3*s+2*w, 9*s+6*w);
ctx.strokeRect(tlX+9*s+7*w, tlY+3*s+3*w, 3*s+2*w, 3*s+2*w);
for (let f = 0; f < 6; f++) {
f9 = f * 9;
x = tlX + face[f][0];
y = tlY + face[f][1];
for (let i = 0; i < 9; i++) {
ctx.fillStyle = col[(i==8 ? f : cc[f9+i])];
ctx.fillRect(x+s*cell[i][0], y+s*cell[i][1], s, s);
}
ctx.fillStyle = coW[f];
ctx.font = "15px Arial ExtraBold";
ctx.fillText(orient[cc[f9+8]], x+s*cell[8][0], y+s+s*cell[8][1], s);
}
}
</script>
JS;
public function out($t, $c) { # output text $t and Rubik $c
out((is_int($t) ? ('<span style="background-color: ' . ($k = Rubik::COL[intdiv($t,10)]) . ';color: ' . Rubik::COW[intdiv($t,10)] . ";\">rot $t = $k" . Rubik::ROT[$t%10]) : $t)
. '</span><br><canvas id="rubikc' . $this->canC . '" width="160" height="120"></canvas>');
$this->canIni .= "\n\t\trubik(document.getElementById('rubikc$this->canC').getContext('2d'), 2, 2, '$c');";
$this->canC++;
}
private $canC = 0, $canIni = '';
public function slvB2() {
outTb();
out(self::JSFUN, str_repeat(' ', 45));
}
public function slvE2() {
outTBEnd();
}
public function rot2($t, $c) { # output text $t and Rubik $c for the rotation table
outTD();
$this->out($t, $c);
}
public function rotE2() {
out("<script type=\"text/javascript\">$this->canIni\n</script>");
$this->canIni = '';
outTR();
}
} # end class RubikOutHTML
class RubikCnt {
public $rubik, $tSlvB, $slvInf, $tSrchB, $rotCC, $rotLTot, $rotNN, $rotInf, $err, $tSr, $tBa;
public function __construct() {
$this->rotLTot = (int) 1e8;
}
public function slvB($i) {
$this->tSlvB = time();
$this->slvInf = $i;
$this->rotCC = $this->rotNN = $this->tSr = $this->tBa = $this->err = 0;
}
public function slvE($c) {}
public function srchB($r) {
$this->rubik = $r;
$this->tSRchB = time();
}
public function srchE($rs) { # after return from search
$this->rotCC += $this->rubik->rotC;
$this->tSr += $this->rubik->tSrch;
$this->tBa += $this->rubik->tBack;
if (is_array($rs)) {
$this->rotNN += count($rs);
$this->rotInf = 'search found ' . count($rs) . ' rots ' . a2str($rs);
} elseif (! $this->err) {
$this->rotInf = $this->err = 'not found ' . ($rs ?? 'returned null');
}
}
public function rot($t, $c) {} # one rotation $t to rubik $c
public function rotE() {} # after last rotation of one search
} # end class RubikCnt
class RubikOut extends RubikCnt {
public function __construct() {
$this->o = OutHtml ? new RubikOutHtml : new RubikOutCli;
}
public function out($t, $c) { # output text $t and Rubik $c
$this->o->out($t, $c);
}
public function slvB($i) {
parent::slvB($i);
$this->o->slvB2();
}
public function slvE($c) {
parent::slvE($c);
$tE = time();
out($this->err ? $this->err : "solved $this->rotNN rots,", sprintf('%7.2e tries,', $this->rotCC));
out($tE-$this->tSlvB, "secs,", sprintf('%.3f back, %.3f search at', $this->tBa/1e9, $this->tSr/1e9, 3)); #, date('Y-m-d\TH:i:s', $tE));
if (! $this->err)
$this->o->rot2('end', $c);
$this->o->rotE2();
$this->o->slvE2();
}
public function srchB($r) {
parent::srchB($r);
out($r->cm1);
out($r->cm2);
}
public function srchE($rs) { # after return from search
parent::srchE($rs);
$tE = time();
out($this->err ? $this->err : "solved {$this->rubik->tTo}/{$this->rubik->tTc}", sprintf(', with %7.2e tries', $this->rubik->rotC));
out("$this->slvInf $this->rotInf");
out($this->rubik->bkI);
out($tE-$this->tSRchB, "secs,", $tE-$this->tSlvB, "tot at", date('Y-m-d\TH:i:s', $tE));
}
public function rot($t, $c) { $this->o->rot2($t, $c); } # one rot
public function rotE() { $this->o->rotE2(); } # after last rot
} # end class RubikOut
class RubikOutSlv extends RubikCnt {
private $m1Inf, $m1RotC;
public function slvB($i) {
parent::slvB($i);
$this->m1RotC = $m1Inf = -1;
}
public function srchE($rs) { # after return from search
parent::srchE($rs);
if ($this->rubik->rotC > $this->m1RotC) {
$this->m1RotC = ($r = $this->rubik)->rotC;
$this->m1Inf = [$r->cm1, $r->cm2, $this->rotInf, [sprintf('%7.2e tries', $r->rotC), ", $r->bkI in", time()-$this->tSRchB, "secs"]];
}
}
public function slvE($c) {
parent::slvE($c);
out($this->slvInf, $this->err ? $this->err : "solved" , "$this->rotNN rots,", sprintf('%7.2e tries,', $this->rotCC));
out(time() - $this->tSlvB, "secs,", sprintf('%.3f back, %.3f search at', $this->tBa/1e9, $this->tSr/1e9, 3));
foreach ($this->m1Inf as $i)
is_array($i) ? out(...$i) : out($i);
}
} # end class RubikOutSlv
outBegin();
Rubik::config();
if (0) {
$r = 'abcdefgh1ijklmnop1qrstuvwx1yzABCDEF1GHIJKLMN1OPQRSTUV1';
Rubik::linO($r);
for ($x=0; $x<6; $x++) {
out ("\nrot $x");
$n = $r;
for ($y=1; $y<5; $y++) {
$n = Rubik::$fRot[$x]($n);
out ("$y n $n", strlen($n));
#Rubik::linO($n);
Rubik::linR($n, $x);
}
($n === $r) or err("n !== r");
}
$t = str_pad('', 54, 'f');
foreach (Rubik::$rTrg as $x => $cc)
foreach ($cc as $c)
$t[$c] = '0123456789abcdefghijklmnopqrstuvwxyz'[$x];
out("rTrg", $t);
Rubik::linO($t);
}
if (0) {
$srch = 'searcC';
$c = Rubik::test(0);
$tXX=0;
$tt = [];
$tB00 = time();
while ($tt = Rubik::trgNxt($tt, $tXX++, $c)) {
$tc = count($tt);
$tB = time();
out("the problem for $tc target colourCells ", date('Y-m-d\TH:i:s', $tB));
Rubik::linO($c);
for ($d=1; $d<15; $d++) {
if (! $s = Rubik::$srch($c, $d, $tt)) {
out("search $d notfound", sprintf('%7.2e =', Rubik::$rC), Rubik::$rC, date('Y-m-d\TH:i:s'));
} else {
$tE = time();
out("search $d for $tc targets found", count($s) - 1, "rots,", sprintf('%7.2e =', Rubik::$rC), Rubik::$rC, "tries," ,$tE-$tB, "secs,", $tE-$tB00, "tot at", date('Y-m-d\TH:i:s', $tE));
outTB();
outTCF('r');
outTRD(...range(0, count($s)-1));
outTRD(...array_values($s));
outTR();
foreach($s as $v => $vRR) {
$w = 0;
foreach ($tt as $a => $c)
if ($c !== $v[$a])
$w++;
outTD($w);
}
outTBEnd();
foreach($s as $n => $r) {
out("--- rot $r ------");
Rubik::linO($n);
}
$c = array_key_last($s);
continue 2;
}
}
err("search not found for $tc, rC", Rubik::$rC);
}
}
if (1) {
$o = new RubikOut;
$o->rotLTot = 1e9;
Rubik::solve(0, $o, 2);
/* foreach ([0,1, 909] as $t ) {
outH("test $t");
$o = new RubikOutSlv;
foreach( [1,2,3,4,5,7,10, 20] as $x) {
out("rotCC $o->rotCC, lim $o->rotLTot");
Rubik::solve($t, $o, $x);
$o->rotLTot = min($o->rotLTot, 2 * $o->rotCC);
}
} */
# for($x=1; $x<=4; $x++)
# Rubik::solve(1, $o, $x); */
}
if (0) {
$c = Rubik::test(3);
$tB00 = time();
$rotCC = $rotNN = $tSr = $tBa = 0;
Rubik::linIO('b', '');
while (($r = new Rubik($c, 2))->tTo > $r->tFr) {
$tB = time();
$nn = $r->{Rubik::SRCH}();
$tE = time();
if (is_null($nn))
err("count not solve $r->tTo/$r->tTc targets,", $tE-$tB, "secs,", $tE-$tB00, "tot at", date('Y-m-d\TH:i:s', $tE));
out("solved $r->tTo/$r->tTc in", count($nn), "rots,", sprintf('%7.2e tries', $r->rotC));
out($r->bkI);
out($tE-$tB, "secs,", $tE-$tB00, "tot at", date('Y-m-d\TH:i:s', $tE));
Rubik::linIO('from', $d = $c);
foreach($nn as $n) {
$n = $n % 100;
for ($k = $n % 10; $k > 0; $k--)
$d = Rubik::$fRot[intdiv($n, 10) ]($d);
Rubik::linIO($n, $d);
}
Rubik::linIO('r', '');
$c = $d;
$rotCC += $r->rotC;
$rotNN += count($nn);
$tSr += $r->tSrch;
$tBa += $r->tBack;
}
out("solved $rotNN rots,", sprintf('%7.2e tries,', $rotCC));
out($tE-$tB00, "secs,", sprintf('%.3f back, %.3f search at', $tBa/1e9, $tSr/1e9, 3)); #, date('Y-m-d\TH:i:s', $tE));
Rubik::linIO('end', $c);
Rubik::linIO('r', '');
Rubik::linIO('e', '');
}
err('tst end');
Rubik::linO($c);
#err(config());
# echo dechex($f(0x123436789)) . "\n";
/*
for ($i=0; $i<6; $i++)
rsh($i, $r);
exit();
foreach (Rubik::$rRot as $fa => $fu) {
out("rRot $fa");
Rubik::linO($fu($r));
}
err('endTst');
*/
Rubik::linO($r);
for ($x=0; $x<6; $x++) {
out ("\nrot $x");
$n = $r;
for ($y=1; $y<5; $y++) {
$n = Rubik::$fRot[$x]($n);
out ("$y n $n", strlen($n));
Rubik::linL($n);
Rubik::linR($n, $x);
}
($n === $r) or err("n !== r");
}
exit();
$n =$rot[0]($r);
ll($n);
for ($x=1; $x<7; $x++) {
out("--- rot $x");
for ($y=0; $y<4; $y++) {
$rot[$x]();
ll();
}
}