Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
solve NRG boards using Dijkstra's + cycling algs
stdout: * '0 '1 '2 '3 '4 '5 '6 '7 X X X '8 X X X K=3,diameter=34,BFS time=38 ncombos=362880 0:1 22:8 24:22 26:14 28:26 30:44 32:42 34:12 44:34 46:222 48:327 50:482 52:786 54:1085 56:1017 58:940 60:729 62:594 64:370 66:244 68:870 70:2544 72:3704 74:6404 76:7326 78:7966 80:8666 82:7676 84:6778 86:5826 88:4318 90:3964 92:6666 94:9369 96:14164 98:16066 100:12729 102:11741 104:11267 106:8449 108:7138 110:4610 112:2962 114:1310 116:1080 118:508 120:276 122:24 124:32 126:8 # combos reached=181440 Warning: ncombos=362880!=reached=181440 (could be the result of parity restriction). diameter=126 BFS time (ms)=14505 test array: [1, 2, 3, 4, 5, 6, 7, 8, 0] URRUURURUURRUURRUURRUURDLDDLLD DRDRDLLDDLLDDLLDDLLDLULU ULDDRRDRUURRUURRUURRUURULLUURD RDDRRDRUURRUURRUURRUURULLUUL * 0 1 2 3 '4 5 6 7 8 '9 X X X X '10 X X X X '11 X X X X K=3,diameter=44,BFS time=22 K=4,diameter=48,BFS time=285 ncombos=11880 0:1 30:1 32:36 34:21 35:24 36:38 37:17 38:64 39:24 40:72 41:24 42:92 43:27 44:98 45:8 46:30 48:1 62:4 64:292 65:24 66:460 67:500 68:704 69:398 70:944 71:754 72:818 73:622 74:718 75:602 76:914 77:464 78:854 79:434 80:696 81:262 82:474 83:122 84:180 85:34 86:26 87:2 # combos reached=11880 diameter=87 BFS time (ms)=14933 test array: [2, 1, 3, 0] LUULDRURULURULURDLDDRDLURULURULDRDDLDDLURDDR LDRRUURULDRDLDLDRULURRULURDLDLDRDLULUR * '0 '1 '2 '3 X '4 '5 '6 '7 X X X X X X X X X X X X X X X K=3,diameter=44,BFS time=7 K=4,diameter=48,BFS time=278 ncombos=40320 0:1 32:4 34:10 35:8 36:32 37:8 38:72 39:14 40:65 41:29 42:32 43:6 44:34 45:2 46:5 48:1 66:20 67:44 68:140 69:162 70:534 71:375 72:947 73:680 74:1174 75:508 76:864 77:342 78:490 79:148 80:219 81:39 82:58 83:17 84:5 86:3 101:230 102:400 103:1348 104:1844 105:2350 106:2126 107:1456 108:1232 109:718 110:766 111:234 112:160 113:98 114:48 115:36 116:14 117:4 118:4 # combos reached=20160 Warning: ncombos=40320!=reached=20160 (could be the result of parity restriction). diameter=118 BFS time (ms)=4589 test array: [2, 1, 3, 4, 5, 6, 7, 0] RDRDRDLURULLURULDRDRDLDRULUULURDLL ULDLULDRDDLDRULURULURDLDDRDLURULURRURD LURRDLULDRURULDDRULLDRURDLULURDD Process finished with exit code 0
- Loading branch information
1 parent
1f5097a
commit b65edc9
Showing
1 changed file
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
import java.util.*; | ||
/** | ||
* consider a NxN NRG Loopover board with a binary matrix A | ||
* where the piece at location (r,c) is solved iff A[r][c]==false | ||
* and the gripped piece is solved and at (gr,gc) | ||
* we say that we are currently at the "state" A | ||
* furthermore, we want to go from state A to state B | ||
* where B is another matrix satisfying (!A[r][c]-->!B[r][c]) for all r,c | ||
* i.e. if A[r][c]==false then B[r][c] must also be false | ||
* we will restrict ourselves to solving every scramble with 3-cycles and 2,2-cycles (more generally, blobs) | ||
* additionally, we never make any already solved pieces become unsolved, except when in the process of performing a blob | ||
* i.e. after each blob, all (r,c) s.t. A[r][c]==true are still in their solved positions | ||
*/ | ||
public class LoopoverNRGCycleDijkstra { | ||
private static int mod(int n, int k) { | ||
return (n%k+k)%k; | ||
} | ||
private static String inv(String mvs) { | ||
StringBuilder out=new StringBuilder(); | ||
for (int i=mvs.length()-1; i>-1; i--) { | ||
char c=mvs.charAt(i); | ||
out.append(c=='D'?'U':c=='R'?'L':c=='U'?'D':'R'); | ||
} | ||
return out.toString(); | ||
} | ||
public static String mvseqStr(List<int[]> S) { | ||
StringBuilder str=new StringBuilder(); | ||
for (int[] m:S) | ||
str.append(" ").append(m[0]==0?"R":"C").append(m[1]).append(m[2]==1?"":m[2]==-1?"'":("("+m[2]+")")); | ||
return str.toString(); | ||
} | ||
public static boolean[][] tobmat(String[] rows) { | ||
boolean[][] out=new boolean[rows.length][rows[0].length()]; | ||
for (int i=0; i<out.length; i++) | ||
for (int j=0; j<rows[i].length(); j++) { | ||
char c=rows[i].charAt(j); | ||
if (c=='0') out[i][j]=false; | ||
else if (c=='1') out[i][j]=true; | ||
else throw new RuntimeException("Not parseable as a binary matrix."); | ||
} | ||
return out; | ||
} | ||
private int R, C; | ||
private int Nfree; | ||
private int[] tofree, freeto; | ||
public int K; | ||
//BFS stuff | ||
public int ncombos; | ||
private int[] depth, par, blobi; | ||
private List<String> blobs; | ||
public int D, diam; | ||
public LoopoverNRGCycleDijkstra(int gr, int gc, String A, String B) { | ||
this(gr,gc,tobmat(A.split(",")),tobmat(B.split(","))); | ||
} | ||
private LoopoverNRGCycleDijkstra(int gr, int gc, boolean[][] A, boolean[][] B) { | ||
long st=System.currentTimeMillis(); | ||
R=A.length; C=A[0].length; | ||
if (R!=C) throw new RuntimeException("Only square board sizes allowed."); //need to refactor LoopoverNRGSetup before I can remove this constraint | ||
if (R!=B.length||C!=B[0].length) throw new RuntimeException("Mismatch in dimensions."); | ||
if (A[gr][gc]) throw new RuntimeException("Gripped piece not marked as solved."); | ||
for (int r=0; r<R; r++) for (int c=0; c<C; c++) | ||
if (!A[r][c]&&B[r][c]) throw new RuntimeException("Set of solved pieces in A does not subset set of solved pieces in B."); | ||
Nfree=0; tofree=new int[R*C]; freeto=new int[R*C]; | ||
for (int r=0; r<R; r++) for (int c=0; c<C; c++) if (A[r][c]) { | ||
tofree[r*C+c]=Nfree; | ||
freeto[Nfree]=r*C+c; | ||
Nfree++; | ||
} | ||
freeto=Arrays.copyOfRange(freeto,0,Nfree); | ||
for (int r=0; r<R; r++) { | ||
for (int c=0; c<C; c++) | ||
System.out.printf("%4s", | ||
r==gr&&c==gc?"*": | ||
(A[r][c]? | ||
((B[r][c]?"":"'"))+tofree[r*C+c] | ||
:"X") | ||
//X: locked; ': piece that this BFS tree tries to solve; *: gripped piece | ||
); | ||
System.out.println(); | ||
} | ||
blobs=new ArrayList<>(); List<int[]> blobactions=new ArrayList<>(); | ||
LoopoverNRGSetup[] bfss={LoopoverNRGSetup.cyc3bfs(R), LoopoverNRGSetup.swap22bfs(R)}; | ||
for (LoopoverNRGSetup bfs:bfss) if (bfs!=null) { //table of all possible 3-cycle algorithms we will need | ||
int offset=blobs.size(); | ||
int T=bfs.tP.length; //the number of pieces that will be cycled | ||
for (int rep=0; rep<Math.pow(Nfree,T); rep++) { | ||
blobs.add(null); | ||
blobactions.add(null); | ||
} | ||
int[] P=bfs.tP; | ||
int[] tuple=new int[T]; | ||
while (tuple[T-1]<Nfree) { | ||
boolean good=true; | ||
for (int i=0; i<T&&good; i++) | ||
for (int j=0; j<i&&good; j++) | ||
if (tuple[i]==tuple[j]) good=false; | ||
if (good) { | ||
int i=tupleCode(tuple); | ||
{ | ||
int[] tl=new int[T]; for (int ii=0; ii<T; ii++) tl[ii]=freeto[tuple[ii]]; | ||
blobs.set(offset+i,bfs.sol(tl,gr,gc)); | ||
} | ||
int[] action=new int[Nfree]; | ||
for (int k=0; k<Nfree; k++) action[k]=k; | ||
for (int p=0; p<T; p++) | ||
action[tuple[p]]=tuple[P[p]]; | ||
//applying the move sequence bfs.sol(tl,gr,gc) moves the k-th free location to the action[k]-th free location | ||
blobactions.set(offset+i,action); | ||
} | ||
tuple[0]++; | ||
for (int ti=0; ti<T-1&&tuple[ti]==Nfree; ti++) { | ||
tuple[ti]=0; | ||
tuple[ti+1]++; | ||
} | ||
} | ||
} | ||
//BFS | ||
K=0; int[] solvedscrm=new int[R*C]; | ||
for (int r=0; r<R; r++) for (int c=0; c<C; c++) if (A[r][c]&&!B[r][c]) solvedscrm[K++]=tofree[r*C+c]; | ||
long tmp=1; | ||
for (int rep=0; rep<K; rep++) | ||
tmp*=Nfree-rep; | ||
if (tmp>400_000_000) throw new RuntimeException("Too many combos: "+tmp); | ||
ncombos=(int)tmp; | ||
depth=new int[ncombos]; Arrays.fill(depth,Integer.MAX_VALUE); par=new int[ncombos]; blobi=new int[ncombos]; | ||
System.out.println("ncombos="+ncombos); | ||
List<Set<Integer>> fronts=new ArrayList<>(); fronts.add(new HashSet<>()); | ||
int solvedcode=comboCode(Arrays.copyOfRange(solvedscrm,0,K)); | ||
fronts.get(0).add(solvedcode); | ||
depth[solvedcode]=0; par[solvedcode]=-1; blobi[solvedcode]=-1; | ||
int reached=0; | ||
for (D=0, diam=0; D<fronts.size(); D++) | ||
if (fronts.get(D)!=null&&fronts.get(D).size()>0) { | ||
diam=Math.max(diam,D); | ||
System.out.println(D+":"+fronts.get(D).size()); | ||
reached+=fronts.get(D).size(); | ||
for (int f:fronts.get(D)) { | ||
int[] scrm=codeCombo(f); | ||
for (int ci = 0; ci< blobs.size(); ci++) if (blobactions.get(ci)!=null) { | ||
int nf=comboCode(scrm,blobactions.get(ci)); | ||
int ndepth=depth[f]+ blobs.get(ci).length(); | ||
if (ndepth<depth[nf]) { | ||
if (depth[nf]!=Integer.MAX_VALUE) | ||
fronts.get(depth[nf]).remove(nf); | ||
depth[nf]=ndepth; | ||
par[nf]=f; | ||
blobi[nf]=ci; | ||
while (ndepth>=fronts.size()) fronts.add(null); | ||
if (fronts.get(ndepth)==null) fronts.set(ndepth,new HashSet<>()); | ||
fronts.get(ndepth).add(nf); | ||
} | ||
} | ||
} | ||
} | ||
System.out.println("# combos reached="+reached); | ||
if (reached!=ncombos) System.out.println("Warning: ncombos="+ncombos+"!=reached="+reached+" (could be the result of parity restriction)."); | ||
System.out.println("diameter="+diam); | ||
System.out.println("BFS time (ms)="+(System.currentTimeMillis()-st)); | ||
} | ||
private int tupleCode(int[] vs) { | ||
int out=0; | ||
for (int i=0, pow=1; i<vs.length; i++, pow*=Nfree) | ||
out+=vs[i]*pow; | ||
return out; | ||
} | ||
private int comboCode(int[] A) { | ||
int[] P=new int[Nfree]; | ||
for (int i=0; i<Nfree; i++) P[i]=i; | ||
int[] L=P.clone(); | ||
int out=0; | ||
for (int i=Nfree-1, pow=1; i>=Nfree-K; i--) { | ||
//swap idxs i and L[A[i-(N-K)]] in P | ||
int j=L[A[i-(Nfree-K)]]; | ||
int pi=P[i];//, pj=P[j]; | ||
//P[i]=pj; //<--idx i will never be touched again | ||
P[j]=pi; | ||
L[pi]=j; | ||
//L[pj]=i; | ||
//J_i==j | ||
out+=pow*j; | ||
pow*=i+1; | ||
} | ||
return out; | ||
} | ||
private int comboCode(int[] A, int[] f) { | ||
int[] P=new int[Nfree]; | ||
for (int i=0; i<Nfree; i++) P[i]=i; | ||
int[] L=P.clone(); | ||
int out=0; | ||
for (int i=Nfree-1, pow=1; i>=Nfree-K; i--) { | ||
int j=L[f[A[i-(Nfree-K)]]]; | ||
int pi=P[i]; | ||
P[j]=pi; | ||
L[pi]=j; | ||
out+=pow*j; | ||
pow*=i+1; | ||
} | ||
return out; | ||
} | ||
private int[] codeCombo(int code) { | ||
int[] P=new int[Nfree]; | ||
for (int i=0; i<Nfree; i++) P[i]=i; | ||
for (int v=Nfree; v>Nfree-K; v--) { | ||
int i=v-1, j=code%v; | ||
code/=v; | ||
int pi=P[i]; P[i]=P[j]; P[j]=pi; | ||
} | ||
int[] out=new int[K]; | ||
System.arraycopy(P,Nfree-K,out,0,K); | ||
return out; | ||
} | ||
public String solveseq(int code) { | ||
if (depth[code]==Integer.MAX_VALUE) | ||
throw new RuntimeException("No solution."); | ||
StringBuilder out=new StringBuilder(); | ||
for (int c=code; depth[c]>0; c=par[c]) | ||
out.append(inv(blobs.get(blobi[c]))).append(" "); | ||
return out.toString(); | ||
} | ||
public String solveseq(int[] scramble) { | ||
return solveseq(comboCode(scramble)); | ||
} | ||
private String test() { | ||
int[] tscrm=new int[K]; | ||
for (int i=0; i<K; i++) tscrm[i]=(i+1)%K; | ||
if (K%2==0) { | ||
int tmp=tscrm[0]; tscrm[0]=tscrm[1]; tscrm[1]=tmp; | ||
} | ||
System.out.println("test array: "+Arrays.toString(tscrm)); | ||
return solveseq(tscrm); | ||
} | ||
public static void main(String[] args) { | ||
//4x4 NRG, JKL NOP already solved (takes at most 24 moves) | ||
System.out.println(new LoopoverNRGCycleDijkstra(0,0,"0111,1111,1000,1000","0000,0000,0000,0000").test()); //solve BCD EFGH I M | ||
|
||
//5x5 NRG, LMNO QRST VWXY already solved (takes at most 21+24+66=111 moves) | ||
System.out.println(new LoopoverNRGCycleDijkstra(0,0,"01111,11111,10000,10000,10000","01111,01111,00000,00000,00000").test()); //solve F K P U | ||
System.out.println(new LoopoverNRGCycleDijkstra(0,0,"01111,01111,00000,00000,00000","00000,00000,00000,00000,00000").test()); //solve BCDE GHIJ | ||
} | ||
} |