import { campoDeFluxo, direcaoNoCampo, lerMapa } from "../mapa"; import { AMBIENTES, ESCRITORIO, PORTAS } from "./escritorio"; const MAPA = lerMapa(ESCRITORIO); const { largura, altura } = MAPA; const andavel = (bloqueio: Uint8Array, x: number, z: number) => x >= 0 && z >= 0 && x < largura && z < altura && bloqueio[z * largura + x] === 0; /** Quantas células andáveis a busca em largura alcança a partir do início. */ function alcancaveis(bloqueio: Uint8Array): number { const inicio = Math.floor(MAPA.inicio[1]) * largura + Math.floor(MAPA.inicio[0]); const visto = new Uint8Array(largura * altura); const fila = [inicio]; visto[inicio] = 1; for (let k = 0; k < fila.length; k++) { const x = fila[k] % largura; const z = Math.floor(fila[k] / largura); for (const [dx, dz] of [ [1, 0], [-1, 0], [0, 1], [0, -1], ]) { const i = (z + dz) * largura + x + dx; if (andavel(bloqueio, x + dx, z + dz) && !visto[i]) { visto[i] = 1; fila.push(i); } } } return fila.length; } const totalAndavel = MAPA.solido.filter((c) => c === 0).length; describe("mapa do escritório", () => { it("tem pelo menos 40 × 28 células e é lido por lerMapa", () => { expect(largura).toBeGreaterThanOrEqual(40); expect(altura).toBeGreaterThanOrEqual(28); expect(() => lerMapa(ESCRITORIO)).not.toThrow(); }); it("tem os 7 ambientes com nome", () => { const nomes = AMBIENTES.map((a) => a.nome); for (const nome of [ "recepção", "área de mesas", "sala de reunião", "copa", "sala do servidor", "diretoria", "almoxarifado", ]) { expect(nomes).toContain(nome); } }); it("tem 6 ou mais nascedouros, todos a 8 m ou mais do início", () => { expect(MAPA.nascedouros.length).toBeGreaterThanOrEqual(6); for (const [x, z] of MAPA.nascedouros) { expect( Math.hypot(x - MAPA.inicio[0], z - MAPA.inicio[1]), ).toBeGreaterThanOrEqual(8); } }); it("alcança todo o chão a partir do início", () => { expect(alcancaveis(MAPA.solido)).toBe(totalAndavel); }); it("continua alcançando todo o chão sem qualquer uma das portas", () => { expect(PORTAS.length).toBeGreaterThan(0); for (const porta of PORTAS) { const bloqueio = MAPA.solido.slice(); for (let z = porta.z; z < porta.z + porta.altura; z++) { for (let x = porta.x; x < porta.x + porta.largura; x++) { expect(andavel(bloqueio, x, z)).toBe(true); bloqueio[z * largura + x] = 1; } } expect(alcancaveis(bloqueio)).toBe( totalAndavel - porta.largura * porta.altura, ); } }); it("não tem passagem com menos de 2 células: toda célula andável está num 2 × 2 andável", () => { const livre = (x: number, z: number) => andavel(MAPA.solido, x, z); for (let z = 0; z < altura; z++) { for (let x = 0; x < largura; x++) { if (!livre(x, z)) continue; const emQuadrado = [ [-1, -1], [-1, 0], [0, -1], [0, 0], ].some( ([ox, oz]) => livre(x + ox, z + oz) && livre(x + ox + 1, z + oz) && livre(x + ox, z + oz + 1) && livre(x + ox + 1, z + oz + 1), ); expect([x, z, emQuadrado]).toEqual([x, z, true]); } } }); it("o campo de fluxo leva cada nascedouro ao início em menos de 400 passos", () => { const campo = campoDeFluxo(MAPA, MAPA.inicio[0], MAPA.inicio[1]); const alvo = [Math.floor(MAPA.inicio[0]), Math.floor(MAPA.inicio[1])]; for (const [nx, nz] of MAPA.nascedouros) { let x = Math.floor(nx); let z = Math.floor(nz); let passos = 0; while ((x !== alvo[0] || z !== alvo[1]) && passos < 400) { const [dx, dz] = direcaoNoCampo(MAPA, campo, x + 0.5, z + 0.5); x += Math.round(dx); z += Math.round(dz); passos++; } expect([x, z]).toEqual(alvo); expect(passos).toBeLessThan(400); } }); });