One more. This only requires the number of cycles as an input, and not the length of each cycle.
Fortunately this still fixes the permutation sign. The number of targets is given in the preamble.
The hard part is to generate the cycle lengths to ensure each conditional state is equally likely.
For 2 or 3 cubies not in-place there can only be one cycle. For 4 or 5 there are up to two. And so on.
So I have a look-up object containing 1+1+2+2+..+5+5+6=36 different lookup tables for each possible combination.
Each table contains all the possible integer partitions (possible lengths of each cycle), and the total number of permutations with this permutation type. These frequencies can then be used to sample the cycle lengths using the correct weighting.
The code below is not runnable because the look-up object is not given, but you should be able to construct it.
If anyone really wants to run it I can send them the code to construct the look-up object offline.
# Function for Rubik's cube sim with given number of targets
# nEs = number of solved edges
# nEt = number of twisted edges
# nEcyc = number of edge cycles
# nCs = number of solved corners
# nCt = number of twisted corners (either clockwise or anti-clockwise)
# nCcyc = number of corner cycles
# the permutation sign (odd or even) is given by the sign of nXs+nXt+nXcyc
# the signs of nEs+nEt+nEcyc and nCs+nCt+nCcyc must be the same or the cube will not be solvable
# the number of targets is odd or even according to the permutation sign
# for odd-odd you have a parity issue, for even-even there is no parity issue
# the number of targets is almost always equal to 10+nEt-nEs+nEcyc for edges and 6+nCt-nCs+nCcyc for corners
# i believe there are only two exceptions as below
# (a) if your buffer contains a solved piece; this increases the targets by 2 (because if your buffer was somewhere else you would not need to deal with it)
# (b) if the number of cycles is zero; this increases the targets by 2 (because the minus 2 term derives from the first cycle, which would not exist here)
# Usage example:
# cubeSim(nEs=0, nEt=2, nEcyc=3, nCs=1, nCt=2, nCcyc=2)
# Output:
#$EP
#[1] 4 10 8 1 12 3 7 6 9 11 5 2
#
#$EO
#[1] 1 0 1 1 0 1 1 1 1 0 1 0
#
#$CP
#[1] 3 2 1 8 5 4 7 6
#
#$CO
#[1] 2 0 1 1 1 2 2 0
#
cubeSim <- function(nEs, nEt, nEcyc, nCs, nCt, nCcyc)
{
# Bulletproofing
if(nEs + nEt > 12) stop("cannot have more than 12 edges")
if(nCs + nCt > 8) stop("cannot have more than 8 corners")
if(2*nEcyc > (12 - nEs - nEt)) stop("too many cycles for not enough cubies")
if((nEcyc == 0) && ((nEs + nEt) != 12)) stop("for zero cycles number of in-place edges must be 12")
if((nCcyc == 0) && ((nCs + nCt) != 8)) stop("for zero cycles number of in-place corners must be 8")
if(((nEs+nEt+nEcyc+nCs+nCt+nCcyc) %% 2) == 1) stop("unsolvable due to permutation parity")
# Solved, twisted and remaining edges
Es <- sample(1:12, nEs)
Et <- sample(setdiff(1:12, Es), nEt)
Erem <- setdiff(1:12, c(Es,Et))
# Solved, twisted (clockwise or anticlockwise) and remaining corners
Cs <- sample(1:8, nCs)
Ct <- sample(setdiff(1:8, Cs), nCt)
Crem <- setdiff(1:8, c(Cs,Ct))
# Generate EP and EO
nErem <- length(Erem)
if(nErem == 0) {
EP <- 1:12
EO <- rep(0,12); EO[Et] <- 1
if(sum(EO) %% 2 != 0) stop("unsolvable due to edge orientation")
} else if(nErem == 1) {
stop("code error: this should not occur due to bulletproofing")
} else {
# generate vector of edge cycle lengths nEcycVec using partproblu lookup object
obj <- partproblu[[nErem]][[nEcyc]]
indx <- sample.int(length(obj$freq), size = 1, prob = obj$freq)
nEcycVec <- obj$cyclen[,indx]
# generate EP
cycle <- vector("list", length(nEcycVec))
cycle[[1]] <- sample(Erem, nEcycVec[1])
if(length(nEcycVec) > 1) {
EremRd <- Erem
for(k in 2:length(nEcycVec)) {
EremRd <- setdiff(EremRd, cycle[[k-1]])
cycle[[k]] <- sample(EremRd, nEcycVec[k])
}
}
EP <- 1:12
for(k in 1:length(nEcycVec)) EP[cycle[[k]]] <- c(cycle[[k]][-1], cycle[[k]][1])
EO <- rep(0,12); EO[Et] <- 1
EO[Erem[-1]] <- sample(0:1,length(Erem)-1,replace=TRUE)
EO[Erem[1]] <- -sum(EO) %% 2
}
# Generate CP and CO
nCrem <- length(Crem)
if(nCrem == 0) {
CP <- 1:8
CO <- rep(0,8)
if(length(Ct) == 1) stop("unsolvable due to corner orientation")
if(length(Ct) > 1) {
# sample clockwise/anticlockwise twist
CO[Ct[-1]] <- sample(1:2,length(Ct)-1,replace=TRUE)
CO[Ct[1]] <- -sum(CO) %% 3
}
} else if(nCrem == 1) {
stop("code error: this should not occur due to bulletproofing")
} else {
# generate vector of corner cycle lengths nCcycVec using partproblu lookup object
obj <- partproblu[[nCrem]][[nCcyc]]
indx <- sample.int(length(obj$freq), size = 1, prob = obj$freq)
nCcycVec <- obj$cyclen[,indx]
# generate EP
cycle <- vector("list", length(nCcycVec))
cycle[[1]] <- sample(Crem, nCcycVec[1])
if(length(nCcycVec) > 1) {
CremRd <- Crem
for(k in 2:length(nCcycVec)) {
CremRd <- setdiff(CremRd, cycle[[k-1]])
cycle[[k]] <- sample(CremRd, nCcycVec[k])
}
}
CP <- 1:8
for(k in 1:length(nCcycVec)) CP[cycle[[k]]] <- c(cycle[[k]][-1], cycle[[k]][1])
CO <- rep(0,8); CO[Ct] <- sample(1:2,length(Ct),replace=TRUE)
CO[Crem[-1]] <- sample(0:2,length(Crem)-1,replace=TRUE)
CO[Crem[1]] <- -sum(CO) %% 3
}
# Final Cube
list(EP=EP,EO=EO,CP=CP,CO=CO)
}