Development
- Phase 1: HTML and CSS Structure
- Phase 2: API and DOM Tests
- Phase 3: Testing User Interaction
- Phase 4: Adding Symbols
- Phase 5: Implemented Sudoku
- Phase 6: Start Menu and Final Screen
- Phase 7: Final Touches
This is a project to create a Sudoku game based on an API that generates the puzzles and solves them automatically. To give the application some personality and an original touch, the numbers are replaced with symbols.
The new perspective of Sudoku adds a different yet equally playable gaming concept. Although at first it seemed that replacing numbers with symbols might increase the difficulty of the game, after testing, it turned out not to be the case.
GET /api/new-board| Parameter | Type | Description |
|---|---|---|
RapidAPI App |
string |
Required. The API ID on RapidAPI |
Request URL |
string |
Required. rapidapi.com |
X-RapidAPI-Host |
string |
Required. Header provided by RapidAPI |
X-RapidAPI-Key |
enum |
Required. Key based on the user |
diff |
string |
Optional. Puzzle difficulty (default 2) |
GET /api/solve-board| Parameter | Type | Description |
|---|---|---|
RapidAPI App |
string |
Required. The API ID on RapidAPI |
Request URL |
string |
Required. rapidapi.com |
X-RapidAPI-Host |
string |
Required. Header provided by RapidAPI |
X-RapidAPI-Key |
enum |
Required. Key based on the user |
sudo |
string |
Required. Puzzle to be solved |
The initial idea was to create a search page using one of the APIs from the provided list: Disney Characters API. In this search engine, users could find characters and "like" them or add comments.
Upon analyzing the API during idea approval, we found it had only two endpoints:
{
getAllCharacters: 'https://api.disneyapi.dev/characters',
getOneCharacters: 'https://api.disneyapi.dev/characters/:id'
}This did not allow direct searches in the API and posed a problem since the only possible action was to obtain complete information with the first GET. The desired functionality for the project, involving multiple API calls, was too limited and was therefore discarded.
After discarding the first idea, the idea of creating a Sudoku game with the twist of replacing numbers with symbols emerged while researching on RapidAPI.
The first API found was Solve-Sudoku.
This API allowed downloading the puzzle and its solution simultaneously but had a limit of 50 searches per day, and obtaining the "key" required credit card registration since, once the 50 searches were exceeded, a fee would be charged.
This option was discarded as the level of requests during development could exceed 50 searches per day, which could incur a cost.
The first phase consisted of designing possible layouts to present the page and make the layout clear and simple.
The game's visual idea is very simple: a central box with all 81 elements (numbers) inside. Initially, I opted to create a <ul> with 81 <li> elements inside. It was already obvious from the beginning that these elements should not be manually created this way, but in the testing phase, it was easier to visualize.
With CSS, the most obvious step was converting all list elements into a grid. Setting the cell sizes in vw units added a bit of responsiveness, allowing tests on different screen sizes.
Responsiveness is very important in this project as it aims to be a functional game on most devices (prioritizing phones).
No complications here.
This phase involved testing the API and its interaction with the DOM to render all the puzzle elements correctly and identify possible errors.
The API documentation itself provided examples for making calls using axios, fetch, or XMLHttp.
The first test used fetch().then() to execute a call and verify the response saved in a variable.
The returned data consists of an array with 9 elements (representing the 9 rows of the Sudoku), each containing another array with the 9 values of each cell.
{1 item
"response":{2 items
"difficulty":"easy"
"unsolved-sudoku":[9 items
0:[9 items // First row
0:1 // Cell value
1:0
2:0
3:2
4:5
5:0
6:0
7:8
8:9
]
1:[...]9 items
2:[...]9 items
3:[...]9 items
4:[...]9 items
5:[...]9 items
6:[...]9 items
7:[...]9 items
8:[...]9 items
]
}To make rendering elements within each cell easier, the first step was to concatenate each row into a single array containing all values together. In other paid Sudoku APIs, the response tends to be like this without dividing by rows, so if the reference API ever needs to be changed, it will be easier to implement.
This array serves as a reference for the grid so that each value matches its corresponding child element as we have it defined: <ul class="grid">.
With this foundation, we can already print the values.
- Asynchronicity and
fetch: Initial tests involved executing the "getPuzzle" function at the beginning. The behavior was correct, and all elements printed upon the page load, by addingasyncto the script import in the HTML. The problem arose when adding a button to trigger the API call. Finally, it turned out to be easier to request the API through anasyncfunction since, when using a button as a trigger to print the elements on-screen,awaitcould be used to avoid rendering errors due to missing data. Usingfetch().then()made the code longer with too many.then()calls, making it harder to read.
This phase involves implementing the system for modifying the values of empty cells. First, it is necessary to visually highlight the selected cell by creating a variable that defines the selected cell and adding the "selected" class to it, which changes the background color.
To input values for each cell, creating buttons at the bottom with all possible options seemed the most appropriate. Visually, this aligns with the main grid and allows users to play on phones and other devices without adding too many complications.
- We discarded the idea of using an input field since the keyboard covers part of the screen on phones, making it visually uncomfortable. Additionally, replacing numbers with symbols would make this approach extremely inconvenient.
This phase consists of replacing all the numbers with symbols. Each symbol is created with Photoshop to give an Egyptian touch related to the world of space and space travel. We have three symbols from Egyptian hieroglyphs, another three from the symbols used in Star Gate, and another three from the No Man's Sky glyph system.
Each number is replaced by one of the symbols, and with the bottom button bar, we have a reference for each one.
- To keep a reference of which number corresponds to each symbol and other properties, testing was done using HTML data attributes. Finally, we discarded this option since the only relevant information was the number, which was already stored in the "alt" attribute of the image. Other properties were related to the HTML class itself, and therefore, it was not necessary to use data attributes.
At this stage, the main functionality for playing the game has been implemented. We now proceed with testing to see if the solution to the puzzle we provided is correct. The API itself already sends the puzzle solution, so no additional call is needed, as was the case with other APIs.
We apply the following logic to identify errors:
function checkUserSolution() {
let wrongTiles = 0;
const tiles = [].slice.call(grid.children);
for (const [i, tile] of tiles.entries()) {
if (tile.children[0].alt != solvedPuzzle[i]) {
grid.children[i].classList.add("wrong");
wrongTiles++;
}
}
if (wrongTiles > 0) alert(`Wrong answer! you made ${wrongTiles} mistakes`);
menuContainer.style.display = "none";
}The const tiles creates an array with all the cells so that we can check the user's responses. The solution array is already set up to match the indices of the grid, allowing it to correct the incorrect cells by adding the "wrong" class, which changes their background color.
The start menu is a container that covers the entire screen and provides start options and options to submit the puzzle for verification or view any errors made.
We also have the option to load a new puzzle.
Once the puzzle is submitted, the final screen indicates that we have unlocked the correct combination.
We add a timer to record the time it takes to solve the puzzle.
const timerDOM = document.getElementById("timer");
let timer;
let start = false;
let hours = 0;
let minutes = `0${0}`;
let seconds = `0${0}`;
const resetTimer = () => {
minutes = `0${0}`;
seconds = `0${0}`;
hours = 0;
timer = `${hours}:${minutes}:${seconds}`;
timerDOM.innerText = timer;
};
const stopTimer = () => (start = false);
const startTimer = () => (start = true);
setInterval(() => {
if (start) {
seconds++;
seconds < 10 ? (seconds = `0${seconds}`) : (seconds = seconds);
if (seconds == 60) {
minutes++;
seconds = `0${0}`;
seconds = seconds;
minutes < 10 ? (minutes = `0${minutes}`) : (minutes = minutes);
}
if (minutes == 60) {
hours++;
minutes == 60 ? (minutes = `0${0}`) : (minutes = minutes);
}
timer = `${hours}:${minutes}:${seconds}`;
timerDOM.innerText = timer;
}
}, 1000);
export { stopTimer, startTimer, resetTimer, timer };The app sets the start variable to true or false as needed. When the menu is open, the timer is paused as the menu acts like a pause button.
In this phase, we also refactor the code, moving the API call content to a separate file and applying various optimizations.
After some time without working on the project, I revisited it from time to time realizing that the API used was not working anymore or taking too long to respond.
On this update I added a script in the scripts folder that generates a new puzzle and the solution, this function is called instead of the API call. It also has a difficulty parameter.
This made the whole process much faster and more reliable.
The difficulty levels were added to the game, and the user can choose between easy, medium, and hard. The difficulty level is passed as a parameter to the function that generates the puzzle. Based on the number of tiles removed from the original puzzle, the difficulty level is set. 20 tiles are removed for easy, 40 for medium, and 60 for hard.
As the game was being developed, several ideas emerged that could not be implemented:
- Add sounds and music
- User CRUD
- Leaderboard for best scores




