Skip to content

feat: la sesion de duelo, sin tocar el arte del Coliseo - #25

Closed
leocagli wants to merge 1 commit into
mainfrom
duelos-sesion
Closed

feat: la sesion de duelo, sin tocar el arte del Coliseo#25
leocagli wants to merge 1 commit into
mainfrom
duelos-sesion

Conversation

@leocagli

Copy link
Copy Markdown
Collaborator

La capa de sesion de los duelos. No toca el arte de nada: el Coliseo ya
existe como mapa propio y publica todo lo que hace falta.

Que resuelve

Los puntos 1 a 7 del traspaso, salvo el cableado a game.js, que va aparte para
poder revisar de a una cosa por vez.

1. Guardar mapa y posicion previa begin(from)
2. Asignar west/east de forma determinista sideFor()
3. Colocar con duelSpawns, sin copiar numeros spawnFor()
4. Encerrar en arenaBounds, bloquear Q clamp(), blocksExit()
5. Lugar del arbitro refereeSpawn()
6. Replicar al rival ya funciona, ver abajo
7. Devolver al lugar previo end()

Tres decisiones

Los lados se calculan, no se acuerdan. Dos jugadores sin servidor tienen que
llegar al mismo reparto por su cuenta, y cualquier negociacion ("vos oeste, yo
este") es un mensaje que se puede perder o contradecir. Se comparan las dos
identidades y la menor va al oeste: los dos hacen la misma cuenta, en el orden
que sea, y les da lo mismo. Un test lo prueba con 72 pares.

La vuelta se guarda al entrar. Un duelo termina porque alguien gano, porque
se rindio, o porque se corto internet. El ultimo es el que manda el diseno: si el
regreso dependiera de un mensaje de cierre, el que se desconecta quedaria varado
en el Coliseo para siempre. Por eso from se guarda antes de salir. Los tres
motivos vuelven al mismo lugar, y terminar dos veces es inofensivo, porque el
aviso de que el rival se fue puede llegar despues de que el duelo ya termino.

Ninguna coordenada esta escrita en el modulo. Todas salen de MAPS.coliseum
y se devuelven copiadas: mover el arte no obliga a tocar la logica, y nadie puede
correrle los puntos al mapa sin querer. Hay un test para cada mitad de eso.

El punto 6 sale gratis

La capa de presencia ya expone update(mapId, x, y) y others(mapId). Con los
dos jugadores parados en el mapa coliseum, el rival se replica sin una linea
de codigo nueva
. No hace falta abrir combate contra monstruos ni una segunda
arena visual.

Un test empezo rojo y el mapa tenia razon

Los dos lados parecen asimetricos si se los mide contra arenaBounds.center.x:

64 - 40 = 24        87 - 64 = 23

Pero el Coliseo mide 128 de ancho, asi que su centro real cae en 63.5, y
center.x es ese numero redondeado. Contra el centro de verdad:

63.5 - 40 = 23.5    87 - 63.5 = 23.5

Estan bien. El test ahora mide contra el centro geometrico, derivado de los
propios limites, y quedo escrito por que. No hace falta cambiar el mapa.

Verificacion

antes    65 tests, 515 asserts
ahora    82 tests, 630 asserts     (+17 tests, +115 asserts)

npm test          82/82, 630/630
npm run lint      limpio
git diff --check  limpio

Los 17 tests nuevos son todos del modulo nuevo; ninguno de los 65 anteriores
cambio de comportamiento.

Lo que NO hace

No calcula quien gana, no toca la vida de nadie y no habla con la cadena.
docs/coliseum.md pide eso del mapa y vale igual para la sesion: geometria y
estado, nada mas. Duelos, jefe mundial y encuentros con monstruos son sesiones
distintas y no comparten estado aca.

El Coliseo ya existe como mapa propio y publica duelSpawns, arenaBounds y
refereeSpawn. Esto no dibuja nada: se ocupa de quien va de que lado, donde
vuelve cada uno al terminar, y que nadie se escape a las gradas mientras la
pelea vive.

Tres decisiones que ordenan el resto.

Los lados se calculan, no se acuerdan. Dos jugadores sin servidor tienen que
llegar al mismo reparto por su cuenta, y cualquier negociacion es un mensaje que
se puede perder o contradecir. Comparar las dos identidades y que la menor sea
oeste no necesita ningun mensaje: los dos hacen la misma cuenta y les da lo
mismo. Hay un test que lo prueba con 72 pares.

La vuelta se guarda al entrar. Un duelo termina porque alguien gano, porque se
rindio, o porque se corto internet. El ultimo caso es el que manda el diseno: si
el regreso dependiera de un mensaje de cierre, el que se desconecta quedaria
varado en el Coliseo para siempre. Por eso `from` se guarda antes de salir y
alcanza con tenerlo. Los tres motivos vuelven al mismo lugar, y terminar dos
veces es inofensivo.

Ninguna coordenada esta escrita aca. Todas salen de MAPS.coliseum y se devuelven
copiadas, para que mover el arte no obligue a tocar la logica y para que nadie le
corra los puntos al mapa sin querer. Hay un test que lo vigila.

El punto 6 del traspaso sale gratis: la capa de presencia ya expone
update(mapId, x, y) y others(mapId), asi que con los dos jugadores parados en el
mapa `coliseum` el rival se replica sin codigo nuevo.

Un test empezo rojo y el mapa tenia razon: los dos lados se ven asimetricos
contra arenaBounds.center.x, que es 64 redondeado, pero el Coliseo mide 128 y su
centro real cae en 63.5. Contra ese, 40 y 87 estan a 23.5 los dos. El test ahora
mide contra el centro geometrico y quedo documentado por que.

Este commit no toca game.js todavia: es la capa de sesion y sus pruebas. El
cableado del recorrido completo va aparte para que se pueda revisar de a una
cosa por vez.

  antes    65 tests, 515 asserts
  ahora    82 tests, 630 asserts     (+17 tests, +115 asserts)

lint limpio y git diff --check limpio.
Comment thread lib/duel.js
Comment on lines +141 to +145
if (!from || typeof from.mapId !== 'string') {
throw new Error('hace falta saber de donde vino para poder devolverlo')
}

this.from = { mapId: from.mapId, x: from.x, y: from.y }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: begin() accepts from without numeric x/y

begin() only validates that from.mapId is a string, not that x/y are numbers. A caller passing {mapId:'city'} (no coords) is accepted, and this.from stores {x:undefined,y:undefined}; end() then returns those undefined coordinates and the player is sent to an invalid position instead of their origin — the exact 'varado' outcome the design tries to avoid. Validate that from.x and from.y are finite numbers alongside the mapId check.

Was this helpful? React with 👍 / 👎

Comment thread lib/duel.js
Comment on lines +196 to +201
end(reason = 'termino') {
if (this.state === OVER) return this.from ? { ...this.from } : null
this.state = OVER
this.reason = reason
return this.from ? { ...this.from } : null
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: end() on an idle duel silently moves it to OVER

Calling end() before begin() (state IDLE) falls through, sets state=OVER and returns null. A subsequent begin() then throws 'este duelo ya termino', so a duel can be permanently killed before it ever started. Consider guarding end() to only act when state===ACTIVE (return null/no-op when IDLE), so a stray close event before entry can't brick the session.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 2 findings

Introduces the duel session layer with deterministic side assignment, position clamping, and robust return handling. Consider validating numeric x/y coordinates in begin() and handling idle state transitions defensively in end().

💡 Edge Case: begin() accepts from without numeric x/y

📄 lib/duel.js:141-145

begin() only validates that from.mapId is a string, not that x/y are numbers. A caller passing {mapId:'city'} (no coords) is accepted, and this.from stores {x:undefined,y:undefined}; end() then returns those undefined coordinates and the player is sent to an invalid position instead of their origin — the exact 'varado' outcome the design tries to avoid. Validate that from.x and from.y are finite numbers alongside the mapId check.

💡 Edge Case: end() on an idle duel silently moves it to OVER

📄 lib/duel.js:196-201

Calling end() before begin() (state IDLE) falls through, sets state=OVER and returns null. A subsequent begin() then throws 'este duelo ya termino', so a duel can be permanently killed before it ever started. Consider guarding end() to only act when state===ACTIVE (return null/no-op when IDLE), so a stray close event before entry can't brick the session.

🤖 Prompt for agents
Code Review: Introduces the duel session layer with deterministic side assignment, position clamping, and robust return handling. Consider validating numeric x/y coordinates in begin() and handling idle state transitions defensively in end().

1. 💡 Edge Case: begin() accepts from without numeric x/y
   Files: lib/duel.js:141-145

   begin() only validates that from.mapId is a string, not that x/y are numbers. A caller passing {mapId:'city'} (no coords) is accepted, and this.from stores {x:undefined,y:undefined}; end() then returns those undefined coordinates and the player is sent to an invalid position instead of their origin — the exact 'varado' outcome the design tries to avoid. Validate that from.x and from.y are finite numbers alongside the mapId check.

2. 💡 Edge Case: end() on an idle duel silently moves it to OVER
   Files: lib/duel.js:196-201

   Calling end() before begin() (state IDLE) falls through, sets state=OVER and returns null. A subsequent begin() then throws 'este duelo ya termino', so a duel can be permanently killed before it ever started. Consider guarding end() to only act when state===ACTIVE (return null/no-op when IDLE), so a stray close event before entry can't brick the session.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Important

Your trial ends in 7 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

@leocagli

Copy link
Copy Markdown
Collaborator Author

Cierro este porque su contenido ya está integrado en el trabajo local de main, junto
con el #26 y el #48, en un merge hecho fuera de GitHub.

El commit que lo trae es 5d11d53 feat: la sesion de duelo, sin tocar el arte del Coliseo,
y encima de eso se construyó el combate PvP jugable del Coliseo y la preparación de la
liquidación Soroban de los duelos, que no existían cuando abrí este PR.

La rama duelos-sesion queda en el remoto a propósito. Todavía es la única copia de
este trabajo en GitHub: el main local que lo integra está 13 commits adelante y sin
pushear. Cuando eso se empuje, la rama pasa a ser redundante y ahí sí se puede borrar.

Nada de lo que había acá se perdió, sólo llegó por otro camino.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant