The clue used to be the category name, which is the same for a hundred words. Each of the 1000 words now carries a single associated trait: Perro -> Fiel, Cebolla -> Hace llorar, Egipto -> Piramides. 18000 clues across the 18 languages. Files move to version 3; the loader already reads both formats. Verification caught 79 clues that contained the word they were meant to hide and gave it away. Eight were exact matches caused by homonyms that do not exist in Spanish: Schach and Xake mean both chess and check, and the same happened with the Arabic 5G and the Chinese Apple and Spam. The other 71 contained the word as a substring, such as Libro -> "Pagine e segnalibro". All rewritten. banco_pistas_test guards it: per language it checks 10x100 words, no empty clue and no clue containing its own word. One-character words are skipped because any sentence contains them; the Turkish title for the film IT is a bare "O".
81 lines
2.8 KiB
Dart
81 lines
2.8 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// Guardas sobre los ficheros de palabras. Son datos, no código, y una pista
|
|
/// mal escrita le regala la partida al impostor sin que falle nada.
|
|
void main() {
|
|
final ficheros = Directory('assets/words')
|
|
.listSync()
|
|
.whereType<File>()
|
|
.where((f) => f.path.endsWith('.json'))
|
|
.toList();
|
|
|
|
test('hay un fichero de palabras por idioma soportado', () {
|
|
expect(ficheros.length, 18);
|
|
});
|
|
|
|
for (final fichero in ficheros) {
|
|
final nombre = fichero.uri.pathSegments.last;
|
|
final datos =
|
|
json.decode(fichero.readAsStringSync()) as Map<String, dynamic>;
|
|
final categorias = datos['categorias'] as Map<String, dynamic>;
|
|
|
|
group(nombre, () {
|
|
test('declara el formato con pista por palabra', () {
|
|
expect(datos['version'], 3);
|
|
});
|
|
|
|
test('tiene 10 categorías de 100 palabras', () {
|
|
expect(categorias.length, 10);
|
|
for (final categoria in categorias.entries) {
|
|
final palabras =
|
|
(categoria.value as Map<String, dynamic>)['palabras'] as List;
|
|
expect(palabras.length, 100, reason: categoria.key);
|
|
}
|
|
});
|
|
|
|
test('cada palabra trae una pista no vacía', () {
|
|
for (final categoria in categorias.entries) {
|
|
final palabras =
|
|
(categoria.value as Map<String, dynamic>)['palabras'] as List;
|
|
for (final entrada in palabras) {
|
|
expect(
|
|
entrada,
|
|
isA<Map<String, dynamic>>(),
|
|
reason: '${categoria.key}: entrada en formato antiguo',
|
|
);
|
|
final mapa = entrada as Map<String, dynamic>;
|
|
expect((mapa['palabra'] as String).trim(), isNotEmpty);
|
|
expect(
|
|
(mapa['pista'] as String?)?.trim(),
|
|
isNotEmpty,
|
|
reason: '${categoria.key}: ${mapa['palabra']} sin pista',
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('ninguna pista contiene la palabra que debe ocultar', () {
|
|
final fugas = <String>[];
|
|
for (final categoria in categorias.entries) {
|
|
final palabras =
|
|
(categoria.value as Map<String, dynamic>)['palabras'] as List;
|
|
for (final entrada in palabras.cast<Map<String, dynamic>>()) {
|
|
final palabra = (entrada['palabra'] as String).toLowerCase();
|
|
final pista = (entrada['pista'] as String).toLowerCase();
|
|
// Las palabras de una sola letra dan falsos positivos: cualquier
|
|
// frase las contiene y no revelan nada.
|
|
if (palabra.length < 2) continue;
|
|
if (pista.contains(palabra)) {
|
|
fugas.add('${categoria.key}: "$palabra" -> "$pista"');
|
|
}
|
|
}
|
|
}
|
|
expect(fugas, isEmpty, reason: fugas.join('\n'));
|
|
});
|
|
});
|
|
}
|
|
}
|