16 KiB
id, title, challengeType, forumTopicId, dashedName
id | title | challengeType | forumTopicId | dashedName |
---|---|---|---|---|
5e601bf95ac9d0ecd8b94afd | Solucionador de Sudoku | 4 | 462357 | sudoku-solver |
--description--
Construye una aplicación full stack de JavaScript que sea funcionalmente similar a esta: https://sudoku-solver.freecodecamp.rocks/. Trabajar en este proyecto implicará escribir tu código utilizando uno de los siguientes métodos:
- Clona este repositorio de GitHub y completa tu proyecto localmente.
- Usa nuestro proyecto inicial de Replit para completar tu proyecto.
- Usa un constructor de sitios de tu elección para completar el proyecto. Asegúrate de incorporar todos los archivos de nuestro repositorio de GitHub.
Cuando hayas terminado, asegúrate de que una demostración funcional de tu proyecto esté alojado en algún lugar público. Luego, envía la URL en el campo Solution Link
. Opcionalmente, también envía un enlace al código fuente de tu proyecto en el campo GitHub Link
.
--instructions--
- Toda la lógica del rompecabezas puede ir dentro de
/controllers/sudoku-solver.js
- La función
validate
debe tomar una cadena de rompecabezas dada y revisarla para ver si tiene 81 caracteres válidos para la entrada. - La función
check
debe estar validando contra el estado actual del tablero. - La función
solve
debe manejar la resolución de cualquier cadena de rompecabezas válida, no solo las entradas de prueba y soluciones. Se espera que escribas la lógica para resolver esto.
- La función
- Toda la lógica de enrutamiento puede ir a
/routes/api.js
- Ve el archivo
puzzle-strings.js
en/controllers
para algunos rompecabezas de ejemplo que tu aplicación debe resolver - Para ejecutar las pruebas de desafío en esta página, establece
NODE_ENV
atest
sin comillas en el archivo.env
- Para ejecutar las pruebas en la consola, usa el comando
npm run test
. Para abrir la consola de Replit presiona Ctrl+Shift+P (Cmd si estas en Mac) y escribe "open shell"
Escribe las siguientes pruebas en tests/1_unit-tests.js
:
- La lógica maneja una cadena de rompecabezas válida de 81 caracteres
- La lógica maneja una cadena de rompecabezas con caracteres inválidos (no 1-9 o
.
) - La lógica maneja una cadena de rompecabezas que no tiene 81 caracteres de longitud
- La lógica maneja una posición de fila válida
- La lógica maneja una posición de fila inválida
- La lógica maneja una posición de columna válida
- La lógica maneja una posición de columna inválida
- La lógica maneja la ubicación de una región válida (cuadrícula 3x3)
- La lógica maneja la ubicación de una región inválida (cuadrícula 3x3)
- Las cadenas de rompecabezas válidas pasan el solucionador
- Las cadenas de rompecabezas no válidas hacen fallar al solucionador
- El solucionador devuelve la solución esperada para un rompecabezas incompleto
Escribe las siguientes pruebas en tests/2_functional-tests.js
- Resuelve un rompecabezas con una cadena de rompecabezas válida: petición POST a
/api/solve
- Resuelve un rompecabezas con una cadena de rompecabezas faltante: petición POST a
/api/solve
- Resuelve un rompecabezas con caracteres inválidos: petición POST a
/api/solve
- Resuelve un rompecabezas con una longitud incorrecta: petición POST a
/api/solve
- Resuelve un rompecabezas que no se puede resolver: petición POST a
/api/solve
- Comprueba la ubicación de un rompecabezas con todos los campos: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con un conflicto de posición: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con múltiples conflictos de posición: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con todos los conflictos de posición: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con los campos requeridos faltantes: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con caracteres inválidos: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con una longitud incorrecta: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con coordenadas de posición no válidas: petición POST a
/api/check
- Comprueba la ubicación de un rompecabezas con valor de posición no válido: petición POST a
/api/check
--hints--
Debes proporcionar tu propio proyecto, no la URL del ejemplo.
(getUserInput) => {
const url = getUserInput('url');
assert(!/.*\/sudoku-solver\.freecodecamp\.rocks/.test(getUserInput('url')));
};
Puedes realizar una petición POST
/api/solve
con datos del formulario que contienen puzzle
que será una cadena que contiene una combinación de números (1-9) y puntos .
para representar espacios vacíos. El objeto devuelto contendrá una propiedad de solution
con el rompecabezas resuelto.
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output =
'769235418851496372432178956174569283395842761628713549283657194516924837947381625';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'solution');
assert.equal(parsed.solution, output);
};
Si el objeto enviado a /api/solve
no existe puzzle
, el valor devuelto será { error: 'Required field missing' }
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Required field missing';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ notpuzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
Si el rompecabezas enviado a /api/solve
contiene valores que no son números o periodos, el valor devuelto será { error: 'Invalid characters in puzzle' }
async (getUserInput) => {
const input =
'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid characters in puzzle';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
Si el rompecabezas enviado a /api/solve
es mayor o menor que 81 caracteres, el valor devuelto será { error: 'Expected puzzle to be 81 characters long' }
async (getUserInput) => {
const inputs = [
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6.',
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6...'
];
const output = 'Expected puzzle to be 81 characters long';
for (const input of inputs) {
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
}
};
Si el rompecabezas enviado a /api/solve
no es válido o no se puede resolver, el valor devuelto será { error: 'Puzzle cannot be solved' }
async (getUserInput) => {
const input =
'9.9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Puzzle cannot be solved';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
Puedes realizar una petición POST
a /api/check
un objeto que contenga puzzle
, coordinate
, y value
donde coordinate
es la letra del A-I que indica la fila, seguido por un número del 1-9 indicando la columna, y value
es un numero del 1-9.
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '7';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'valid');
assert.isTrue(parsed.valid);
};
El valor devuelto del POST
a /api/check
será un objeto que contenga una propiedad valid
, que es true
si el número puede colocarse en la coordenada proporcionada y false
si el número no lo es. Si es falso, el objeto devuelto también contendrá una propiedad conflict
que contiene las cadenas "row"
, "column"
, y/o "region"
dependiendo de que la ubicación sea inválida.
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '1';
const conflict = ['row', 'column'];
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'valid');
assert.isFalse(parsed.valid);
assert.property(parsed, 'conflict');
assert.include(parsed.conflict, 'row');
assert.include(parsed.conflict, 'column');
};
Si value
enviado a /api/check
ya está situado en puzzle
en esa coordinate
, el valor devuelto será un objeto que contiene una propiedad valid
con true
si value
no está en conflicto.
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'C3';
const value = '2';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'valid');
assert.isTrue(parsed.valid);
};
Si el rompecabezas enviado a /api/check
contiene valores que no son números o puntos, el valor devuelto será { error: 'Invalid characters in puzzle' }
async (getUserInput) => {
const input =
'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '1';
const output = 'Invalid characters in puzzle';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
Si el rompecabezas enviado a /api/check
es mayor o menor que 81 caracteres, el valor devuelto será { error: 'Expected puzzle to be 81 characters long' }
async (getUserInput) => {
const inputs = [
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6.',
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6...'
];
const coordinate = 'A1';
const value = '1';
const output = 'Expected puzzle to be 81 characters long';
for (const input of inputs) {
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
}
};
Si el objeto enviado a /api/check
no existe puzzle
,coordinate
o value
, el valor devuelto será { error: Required field(s) missing }
async (getUserInput) => {
const inputs = [
{
puzzle: '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..',
value: '1',
},
{
puzzle: '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..',
coordinate: 'A1',
},
{
coordinate: 'A1',
value: '1'
}
];
for (const input of inputs) {
const output = 'Required field(s) missing';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input)
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
}
};
Si la coordenada enviada a api/check
no apunta a una celda de la cuadrícula existente, el valor devuelto será { error: 'Invalid coordinate'}
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid coordinate';
const coordinates = ['A0', 'A10', 'J1', 'A', '1', 'XZ18'];
const value = '7';
for (const coordinate of coordinates) {
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
}
};
Si el value
enviado a /api/check
no es un número entre 1 y 9, los valores devueltos serán { error: 'Invalid value' }
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid value';
const coordinate = 'A1';
const values = ['0', '10', 'A'];
for (const value of values) {
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
}
};
Las 12 pruebas unitarias están completas y pasan. Consulta /tests/1_unit-tests.js
para el comportamiento esperado para el que debes escribir pruebas.
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const units = getTests.filter((el) => el.context.includes('UnitTests'));
assert.isAtLeast(units.length, 12, 'At least 12 tests passed');
units.forEach((test) => {
assert.equal(test.state, 'passed', 'Test in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
Las 14 pruebas funcionales están completas y pasan. Consulta /tests/2_functional-tests.js
para la funcionalidad para la que debes escribir pruebas.
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const funcs = getTests.filter((el) =>
el.context.includes('Functional Tests')
);
assert.isAtLeast(funcs.length, 14, 'At least 14 tests passed');
funcs.forEach((test) => {
assert.equal(test.state, 'passed', 'Test in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
--solutions--
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/