Skip to content

Add CrosswordSolver.java new algorithm #5626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* [AllPathsFromSourceToTarget](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java)
* [ArrayCombination](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java)
* [Combination](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/Combination.java)
* [CrosswordSolver](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java)
* [FloodFill](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/FloodFill.java)
* [KnightsTour](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/KnightsTour.java)
* [MazeRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java)
Expand Down Expand Up @@ -616,6 +617,7 @@
* [AllPathsFromSourceToTargetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java)
* [ArrayCombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java)
* [CombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/CombinationTest.java)
* [CrosswordSolverTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java)
* [FloodFillTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java)
* [KnightsTourTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java)
* [MazeRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java)
Expand Down
124 changes: 124 additions & 0 deletions src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.thealgorithms.backtracking;

import java.util.ArrayList;
import java.util.List;

/**
* A class to solve a crossword puzzle using backtracking.
* Example:
* Input:
* puzzle = {
* {' ', ' ', ' '},
* {' ', ' ', ' '},
* {' ', ' ', ' '}
* }
* words = List.of("cat", "dog")
*
* Output:
* {
* {'c', 'a', 't'},
* {' ', ' ', ' '},
* {'d', 'o', 'g'}
* }
*/
public final class CrosswordSolver {
private CrosswordSolver() {
}

/**
* Checks if a word can be placed at the specified position in the crossword.
*
* @param puzzle The crossword puzzle represented as a 2D char array.
* @param word The word to be placed.
* @param row The row index where the word might be placed.
* @param col The column index where the word might be placed.
* @param vertical If true, the word is placed vertically; otherwise, horizontally.
* @return true if the word can be placed, false otherwise.
*/
public static boolean isValid(char[][] puzzle, String word, int row, int col, boolean vertical) {
for (int i = 0; i < word.length(); i++) {
if (vertical) {
if (row + i >= puzzle.length || puzzle[row + i][col] != ' ') {
return false;
}
} else {
if (col + i >= puzzle[0].length || puzzle[row][col + i] != ' ') {
return false;
}
}
}
return true;
}

/**
* Places a word at the specified position in the crossword.
*
* @param puzzle The crossword puzzle represented as a 2D char array.
* @param word The word to be placed.
* @param row The row index where the word will be placed.
* @param col The column index where the word will be placed.
* @param vertical If true, the word is placed vertically; otherwise, horizontally.
*/
public static void placeWord(char[][] puzzle, String word, int row, int col, boolean vertical) {
for (int i = 0; i < word.length(); i++) {
if (vertical) {
puzzle[row + i][col] = word.charAt(i);
} else {
puzzle[row][col + i] = word.charAt(i);
}
}
}

/**
* Removes a word from the specified position in the crossword.
*
* @param puzzle The crossword puzzle represented as a 2D char array.
* @param word The word to be removed.
* @param row The row index where the word is placed.
* @param col The column index where the word is placed.
* @param vertical If true, the word was placed vertically; otherwise, horizontally.
*/
public static void removeWord(char[][] puzzle, String word, int row, int col, boolean vertical) {
for (int i = 0; i < word.length(); i++) {
if (vertical) {
puzzle[row + i][col] = ' ';
} else {
puzzle[row][col + i] = ' ';
}
}
}

/**
* Solves the crossword puzzle using backtracking.
*
* @param puzzle The crossword puzzle represented as a 2D char array.
* @param words The list of words to be placed.
* @return true if the crossword is solved, false otherwise.
*/
public static boolean solveCrossword(char[][] puzzle, List<String> words) {
// Create a mutable copy of the words list
List<String> remainingWords = new ArrayList<>(words);

for (int row = 0; row < puzzle.length; row++) {
for (int col = 0; col < puzzle[0].length; col++) {
if (puzzle[row][col] == ' ') {
for (String word : new ArrayList<>(remainingWords)) {
for (boolean vertical : new boolean[] {true, false}) {
if (isValid(puzzle, word, row, col, vertical)) {
placeWord(puzzle, word, row, col, vertical);
remainingWords.remove(word);
if (solveCrossword(puzzle, remainingWords)) {
return true;
}
remainingWords.add(word);
removeWord(puzzle, word, row, col, vertical);
}
}
}
return false;
}
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.thealgorithms.backtracking;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;

public class CrosswordSolverTest {

@Test
public void testValidPlacement() {
char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};
assertTrue(CrosswordSolver.isValid(puzzle, "cat", 0, 0, true));
assertTrue(CrosswordSolver.isValid(puzzle, "dog", 0, 0, false));
assertFalse(CrosswordSolver.isValid(puzzle, "cat", 1, 2, false));
}

@Test
public void testPlaceAndRemoveWord() {
char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};
CrosswordSolver.placeWord(puzzle, "cat", 0, 0, true);
assertEquals('c', puzzle[0][0]);
assertEquals('a', puzzle[1][0]);
assertEquals('t', puzzle[2][0]);

CrosswordSolver.removeWord(puzzle, "cat", 0, 0, true);
assertEquals(' ', puzzle[0][0]);
assertEquals(' ', puzzle[1][0]);
assertEquals(' ', puzzle[2][0]);
}

@Test
public void testSolveCrossword() {
char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};
List<String> words = Arrays.asList("cat", "dog", "car");
assertTrue(CrosswordSolver.solveCrossword(puzzle, words));

/* Solved crossword:
* c d c
* a o a
* t g r
*/

assertEquals('c', puzzle[0][0]);
assertEquals('a', puzzle[1][0]);
assertEquals('t', puzzle[2][0]);

assertEquals('d', puzzle[0][1]);
assertEquals('o', puzzle[1][1]);
assertEquals('g', puzzle[2][1]);

assertEquals('c', puzzle[0][2]);
assertEquals('a', puzzle[1][2]);
assertEquals('r', puzzle[2][2]);
}

@Test
public void testNoSolution() {
char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};
List<String> words = Arrays.asList("cat", "dog", "elephant"); // 'elephant' is too long for the grid
assertFalse(CrosswordSolver.solveCrossword(puzzle, words));
}
}