library(R.oo)

setMethodS3("rotate", "default", function(x, steps, ...) {
  n <- length(x);
  steps <- steps %% n;
  if (steps == 0)
    return(x);

  partA <- 1:steps;
  partB <- (steps+1):n;
  x[c(partB,partA)];
})

setMethodS3("rotateColumns", "matrix", function(x, steps, ...) {
  idx <- rotate(1:ncol(x), steps=steps);
  x[,idx];
})

setMethodS3("rotateRows", "matrix", function(x, steps, ...) {
  idx <- rotate(1:nrow(x), steps=steps);
  x[idx,];
})


setConstructorS3("Sudoku", function(...) {
  extend(Object(), "Sudoku");
})


setMethodS3("generate", "Sudoku", function(static, squareId=0, hideId=0, ...) {
  # Generate the root square
  X <- matrix(c(
    rotate(1:9,0),
    rotate(1:9,3),
    rotate(1:9,6)
  ), ncol=9, byrow=TRUE)
  X <- rbind(X, rotateColumns(X, 1), rotateColumns(X, 2))

  if (squareId != 0) {  
    set.seed(squareId, kind="Mersenne-Twister");

    # Permute rows and columns
    rows <- sapply(1:3, FUN=function(x) sample(3)) + 3*rep(sample(0:2),each=3)
    cols <- sapply(1:3, FUN=function(x) sample(3)) + 3*rep(sample(0:2),each=3)
    X <- X[rows,cols];
  }
  
  # Assert that everything is correct
  for (kk in 1:9) {
    if (any(duplicated(X[kk,])))
      throw("Duplicated elements in row #", kk);
    if (any(duplicated(X[,kk])))
      throw("Duplicated elements in column #", kk);
    if (any(rowSums(X) != 45))
      throw("Some rows does not sum to 45.");
    if (any(colSums(X) != 45))
      throw("Some columns does not sum to 45.");
  }
  
  Xtrue <- X;
  
  # Empty middle block
  X[4:6,4:6] <- NA;

  set.seed(hideId, kind="Mersenne-Twister");
  
  # For the rest of the blocks, keep 3 or 4 elements
  for (bb in 1:9) {
    rows <- 1:3 + 3*(bb-1) %/% 3;
    cols <- 1:3 + 3*(bb-1) %% 3;
    Xb <- X[rows,cols];
    Xb[sample(1:9,5)] <- NA;  
    X[rows,cols] <- Xb;
  }

  X;
}, static=TRUE)




###########################################################################
# HISTORY:
# 2005-07-10
# o Created.
###########################################################################
