|
|
|
|
|
|
|
|
|
|
|
|
|
|
SOLVED_STATE <- c(1, 2, 3, 4, 5, 6, 7, 8, 0) |
|
|
|
|
|
|
|
|
pos_to_coords <- function(pos) { |
|
|
list(row = (pos - 1) %/% 3 + 1, col = (pos - 1) %% 3 + 1) |
|
|
} |
|
|
|
|
|
coords_to_pos <- function(row, col) { |
|
|
(row - 1) * 3 + col |
|
|
} |
|
|
|
|
|
is_valid_position <- function(row, col) { |
|
|
row >= 1 && row <= 3 && col >= 1 && col <= 3 |
|
|
} |
|
|
|
|
|
|
|
|
create_solvable_puzzle <- function(moves = 100) { |
|
|
puzzle <- SOLVED_STATE |
|
|
blank_pos <- 9 |
|
|
|
|
|
|
|
|
for(i in 1:moves) { |
|
|
coords <- pos_to_coords(blank_pos) |
|
|
valid_moves <- c() |
|
|
|
|
|
|
|
|
directions <- list( |
|
|
list(row = -1, col = 0), |
|
|
list(row = 1, col = 0), |
|
|
list(row = 0, col = -1), |
|
|
list(row = 0, col = 1) |
|
|
) |
|
|
|
|
|
for(dir in directions) { |
|
|
new_row <- coords$row + dir$row |
|
|
new_col <- coords$col + dir$col |
|
|
|
|
|
if(is_valid_position(new_row, new_col)) { |
|
|
valid_moves <- c(valid_moves, coords_to_pos(new_row, new_col)) |
|
|
} |
|
|
} |
|
|
|
|
|
if(length(valid_moves) > 0) { |
|
|
move_pos <- sample(valid_moves, 1) |
|
|
|
|
|
|
|
|
temp <- puzzle[blank_pos] |
|
|
puzzle[blank_pos] <- puzzle[move_pos] |
|
|
puzzle[move_pos] <- temp |
|
|
|
|
|
blank_pos <- move_pos |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if(identical(puzzle, SOLVED_STATE)) { |
|
|
return(create_solvable_puzzle(moves + 20)) |
|
|
} |
|
|
|
|
|
return(puzzle) |
|
|
} |
|
|
|
|
|
|
|
|
is_solved <- function(puzzle) { |
|
|
identical(puzzle, SOLVED_STATE) |
|
|
} |
|
|
|
|
|
|
|
|
get_blank_position <- function(puzzle) { |
|
|
which(puzzle == 0)[1] |
|
|
} |
|
|
|
|
|
|
|
|
get_valid_moves <- function(blank_pos) { |
|
|
coords <- pos_to_coords(blank_pos) |
|
|
valid_moves <- c() |
|
|
|
|
|
directions <- list( |
|
|
list(row = -1, col = 0), |
|
|
list(row = 1, col = 0), |
|
|
list(row = 0, col = -1), |
|
|
list(row = 0, col = 1) |
|
|
) |
|
|
|
|
|
for(dir in directions) { |
|
|
new_row <- coords$row + dir$row |
|
|
new_col <- coords$col + dir$col |
|
|
|
|
|
if(is_valid_position(new_row, new_col)) { |
|
|
valid_moves <- c(valid_moves, coords_to_pos(new_row, new_col)) |
|
|
} |
|
|
} |
|
|
|
|
|
return(valid_moves) |
|
|
} |
|
|
|
|
|
|
|
|
move_tile <- function(puzzle, tile_position) { |
|
|
blank_pos <- get_blank_position(puzzle) |
|
|
valid_moves <- get_valid_moves(blank_pos) |
|
|
|
|
|
if(tile_position %in% valid_moves) { |
|
|
|
|
|
new_puzzle <- puzzle |
|
|
new_puzzle[blank_pos] <- puzzle[tile_position] |
|
|
new_puzzle[tile_position] <- 0 |
|
|
|
|
|
return(list(puzzle = new_puzzle, moved = TRUE)) |
|
|
} |
|
|
|
|
|
return(list(puzzle = puzzle, moved = FALSE)) |
|
|
} |
|
|
|
|
|
|
|
|
is_solvable <- function(puzzle) { |
|
|
|
|
|
tiles <- puzzle[puzzle != 0] |
|
|
inversions <- 0 |
|
|
|
|
|
for(i in 1:(length(tiles) - 1)) { |
|
|
for(j in (i + 1):length(tiles)) { |
|
|
if(tiles[i] > tiles[j]) { |
|
|
inversions <- inversions + 1 |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return(inversions %% 2 == 0) |
|
|
} |
|
|
|
|
|
|
|
|
validate_puzzle <- function(puzzle) { |
|
|
|
|
|
if(length(puzzle) != 9) { |
|
|
return(FALSE) |
|
|
} |
|
|
|
|
|
|
|
|
expected_tiles <- 0:8 |
|
|
if(!setequal(puzzle, expected_tiles)) { |
|
|
return(FALSE) |
|
|
} |
|
|
|
|
|
|
|
|
if(!is_solvable(puzzle)) { |
|
|
return(FALSE) |
|
|
} |
|
|
|
|
|
return(TRUE) |
|
|
} |
|
|
|
|
|
|
|
|
generate_test_puzzles <- function(n = 5) { |
|
|
puzzles <- list() |
|
|
|
|
|
for(i in 1:n) { |
|
|
repeat { |
|
|
puzzle <- create_solvable_puzzle() |
|
|
if(validate_puzzle(puzzle) && !is_solved(puzzle)) { |
|
|
puzzles[[i]] <- puzzle |
|
|
break |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return(puzzles) |
|
|
} |
|
|
|
|
|
|
|
|
get_puzzle_stats <- function(puzzle) { |
|
|
blank_pos <- get_blank_position(puzzle) |
|
|
valid_moves <- get_valid_moves(blank_pos) |
|
|
|
|
|
return(list( |
|
|
blank_position = blank_pos, |
|
|
valid_moves = valid_moves, |
|
|
num_valid_moves = length(valid_moves), |
|
|
is_solved = is_solved(puzzle), |
|
|
is_valid = validate_puzzle(puzzle) |
|
|
)) |
|
|
} |