Espamática
ZX SpectrumEnsamblador Z80Retro

0x05 Ensamblador ZX Spectrum OXO – yo contra el Spectrum

En este capítulo de Ensamblador ZX Spectrum OXO, vamos a implementar la partida a un solo jugador.

Una vez que podemos jugar contra amigos y familiares queda la posibilidad de que no tengamos con quién, de ahí la necesidad de que se pueda jugar contra el Spectrum.

Yo contra el Spectrum

El grueso de esta implementación lo escribimos en Game.asm, empezando por una rutina que genera números pseudo aleatorios entre uno y nueve, para que cuando el Spectrum empieza la partida, lo haga de distinta manera.

; -------------------------------------------------------------------
; Obtiene un número pseudo aleatorio entre 1 y 9.
;
; Retorno:  A   ->  Número obtenido.
; -------------------------------------------------------------------
GetRandomN:
ld      a, r                ; A = R
and     $0f                 ; Deja bits 0 a 3
inc     a                   ; A+=1
cp      $0a                 ; ¿A > 9?
ret     c                   ; No, sale
rra                         ; A/=2, porque Carry = 0, si no SRL A
ret                         ; Sale

GetRandomN obtiene un número pseudo aleatorio entre uno y nueve, apoyándose en el registro R. En la línea RRA, tal y como se ve en los comentarios, dividimos A entre dos porque el acarreo está a cero y porque solo lo hacemos una vez, si no fuera así deberíamos hacerlo con SRL A. RRA ocupa un byte y tarda cuatro ciclos de reloj, SRL A es justo el doble. Sólo se divide entre dos si el número obtenido es mayor de 9.

La parte que realiza los movimientos del Spectrum es muy larga, ya que son diversas las combinaciones que se evalúan, por lo que vamos a verla por bloques. Aún así, y dado que las semejanzas entre distintos bloques es mucha, vamos a explicar sólo los más significativos.

; -------------------------------------------------------------------
; Mueve ZX Spectrum.
;
; Altera el valor de los registros AF, BC e IX.
; -------------------------------------------------------------------
ZxMove:
ld      a, (MoveCounter)    ; A = movimientos
or      a                   ; ¿Movimientos = 0?
jr      nz, zxMove_center   ; No, salta
call    GetRandomN          ; A = número entre 1 y 9
add     a, '0'              ; A = código ascii
ld      c, a                ; C = A
call    ToMove              ; Mueve a celda
ret                         ; Sale
zxMove_center:
cp      $01                 ; ¿Movimientos > 1?
jr      nz, zxMove_cont     ; Sí, salta
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale

En esta primera parte evaluamos si es el primer movimiento de la partida, y si es así obtenemos un número aleatorio entre uno y nueve y movemos a esa celda. Si es el segundo movimiento intentamos mover a la celda cinco (centro). Si no se dan estas condiciones, seguimos con las comprobaciones de si el Spectrum puede ganar o peder.

zxMove_cont:
ld      ix, Grid-$01        ; IX = dir Grid-1
ld      b, $20              ; B = valor Spectrum puede ganar
call    zxMoveToWin_123     ; Movimiento para ganar Spectrum
ret     z                   ; Si es válido, sale
ld      b, $02              ; B = valor jugador 1 puede ganar
call    zxMoveToWin_123     ; Movimiento para evitarlo
ret     z                   ; Si es válido sale
jp      zxMoveDefence_diagonally ; Movimientos defensivos

Recordad que los movimientos del jugador dos, en este caso el Spectrum, se señalan en el bit cuatro de las celdas, por eso cargamos $20 en B, que sería el valor si hubiera dos celdas ocupadas en una línea por el Spectrum.

Llamamos a zxMoveToWin_123 para que el Spectrum, si puede ganar, realice el movimiento.

Si no ha ganado el Spectrum, cargamos dos en B para evaluar si el jugador uno tiene movimiento para ganar. Llamamos por segunda vez a zxMoveToWin_123 para evitar que gane.

Si no se ha dado ninguno de los casos, pasamos a la defensiva.

; -------------------------------------------------------------------
; Evalúa si el Spectrum tiene movimiento para ganar.
; -------------------------------------------------------------------
zxMoveToWin_123:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$02)         ; A+= valor celda 2
add     a, (ix+$03)         ; A+= valor celda 3
cp      b                   ; ¿A = B?
jp      nz, zxMoveToWin_456 ; No, salta
; Spectrum puede ganar
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 2
call    ToMove              ; Mueve a celda 2
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret                         ; Sale

Si el Spectrum ocupa dos casillas, intentamos mover a la casilla uno, si no es correcto a la dos y si no a la tres. Para ir de una casilla a la siguiente incrementamos C, numéricamente son contiguas.

Las comprobaciones para las combinaciones cuatro, cinco, seis y siete, ocho, nueve, son iguales a la anterior.

zxMoveToWin_456:
ld      a, (ix+$04)         ; A = valor celda 4
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$06)         ; A+= valor celda 6
cp      b                   ; ¿A = B?
jr      nz, zxMoveToWin_789 ; No, salta
; Spectrum puede ganar
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret                         ; Sale

zxMoveToWin_789:
ld      a, (ix+$07)         ; A = valor celda 7
add     a, (ix+$08)         ; A+= valor celda 8
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A = B?
jr      nz, zxMoveToWin_147 ; No, salta
; Spectrum puede ganar
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 8
call    ToMove              ; Mueve a celda 8
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret                         ; Sale

El resto de comprobaciones cambian ligeramente.

zxMoveToWin_147:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$04)         ; A+= valor celda 4
add     a, (ix+$07)         ; A+= valor celda 7
cp      b                   ; ¿A=B?
jr      nz, zxMoveToWin_258 ; No, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret                         ; Sale

Al no estar las casillas contiguas numéricamente, en la parte del final en lugar de incrementar C cargamos cada tecla. Desde aquí, todas las comprobaciones para saber si el Spectrum puede ganar o perder, son iguales.

zxMoveToWin_258:
ld      a, (ix+$02)         ; A = valor celda 2
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$08)         ; A+= valor celda 8
cp      b                   ; ¿A=B?
jr      nz, zxMoveToWin_369 ; No, salta
ld      c, KEY2             ; C = tecla 2
call    ToMove              ; Mueve a celda 2
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY8             ; C = tecla 8
call    ToMove              ; Mueve a celda 8
ret                         ; Sale
    
zxMoveToWin_369:
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$06)         ; A+= valor celda 6
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A=B?
jr      nz, zxMoveToWin_159 ; No, salta
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale
ld      c, KEY6             ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret     z                   ; Si es correcto, sale
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret                         ; Sale
    
zxMoveToWin_159:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A=B?
jr      nz, zxMoveToWin_357 ; No, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret                         ; Sale

zxMoveToWin_357:
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$07)         ; A+= valor celda 7
cp      b                   ; ¿A=B?
ret     nz
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret                         ; Sale

El conjunto de rutinas zxMoveToWin nos sirve tanto para saber si el Spectrum puede ganar y así realizar el movimiento para ello, como para saber si el Spectrum puede perder y mover para evitarlo.

Si tanto el jugador uno como el Spectrum no tienen posibilidad de ganar, nos intentamos anticipar a la jugada del jugador uno. Primero comprobamos si el jugador hace una jugada diagonal ocupando los dos vértices, y si es así realizamos un bloqueo en cruz.

; -------------------------------------------------------------------
; Movimiento defensivo diagonal.
; -------------------------------------------------------------------
zxMoveDefence_diagonally:
; Comprueba si jugador 1 tiene fichas en diagonal
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A = B?
jr      z, zxMoveDefence_crossBlock   ; Sí, mov diagonal, salta
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$07)         ; A+= valor celda 7
cp      b                   ; ¿A = B?
jr      nz, zxMoveDefence_crossBlock1 ; No, salta

zxMoveDefence_crossBlock:
; Bloqueo en cruz
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale
ld      c, KEY6             ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret     z                   ; Si es correcto, sale
ld      c, KEY2             ; C = tecla 2
call    ToMove              ; Mueve a celda 2
ret     z                   ; Si es correcto, sale
ld      c, KEY8             ; C = tecla 8
call    ToMove              ; Mueve a celda 8
ret     z                   ; Si es correcto, sale

Luego comprobamos si ha intentado una jugada diagonal con el centro ocupado, y si es así, dependiendo de que vértice tenga ocupado, bloqueamos la vertical.

; -------------------------------------------------------------------
; Movimiento defensivo de diagonal con el centro ocupado.
; -------------------------------------------------------------------
zxMoveDefence_crossBlock1:
ld      a, (ix+$05)         ; A = valor celda 5
and     $0f                 ; A = valor jugador 1
jr      z, zxMoveDefence_cornerBlock16  ; Z = no ocupada, salta
ld      a, (ix+$01)         ; A = valor celda 1
and     $0f                 ; A = valor jugador 1
jr      z, zxMoveDefence_crossBlock3    ; Z = no ocupada, salta
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale

zxMoveDefence_crossBlock3:
ld      a, (ix+$03)         ; A = valor celda 3
and     $0f                 ; A = valor jugador 1
jr      z, zxMoveDefence_crossBlock7    ; Z = no ocupada, salta
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret     z                   ; Si es correcto, sale

zxMoveDefence_crossBlock7:
ld      a, (ix+$07)         ; A = valor celda 7
and     $0f                 ; A = valor jugador 1
jr      z, zxMoveDefence_crossBlock9    ; Z = no ocupada, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale

zxMoveDefence_crossBlock9:
ld      a, (ix+$09)         ; A = valor celda 9
and     $0f                 ; A = valor jugador 1
jr      z, zxMoveDefence_cornerBlock16  ; Z = no ocupada, salta
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale

Si el jugador uno no intentó un movimiento diagonal, vemos si lo intentó en cruz, y procuramos bloquearlo.

; -------------------------------------------------------------------
; Movimiento defensivo en cruz.
; -------------------------------------------------------------------
zxMoveDefence_cornerBlock16:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$06)         ; A+= valor celda 6
cp      b                   ; ¿A = B?
jr      nz, zxMoveDefence_cornerBlock34 ; No, no mov cruz, salta
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale

zxMoveDefence_cornerBlock34:
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$04)         ; A+= valor celda 4
cp      b                   ; ¿A = B?
jr      nz, zxMoveDefence_cornerBlock67 ; No, no mov cruz, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale

zxMoveDefence_cornerBlock67:
ld      a, (ix+$06)         ; A = valor celda 6
add     a, (ix+$07)         ; A+= valor celda 7
cp      b                   ; ¿A = B?
jr      nz, zxMoveDefence_cornerBlock49 ; No, no mov cruz, salta
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret     z                   ; Si es correcto, sale

zxMoveDefence_cornerBlock49:
ld      a, (ix+$04)         ; A = valor celda 4
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A = B?
jr      nz, zxMoveDefence_cornerBlock1827 ; No, no mov cruz, salta
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale

zxMoveDefence_cornerBlock1827:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$08)         ; A+= valor celda 8
cp      b                   ; ¿A = B?
jr      z, zxMoveDefence_cornerBlock1827Cont ; Sí, mov cruz, bloquear
ld      a, (ix+$02)         ; A = valor celda 2
add     a, (ix+$07)         ; A+= Valor celda 7
cp      b                   ; ¿ A= B?
jr      nz, zxMoveDefence_cornerBlock2938 ; No, no mov cruz, salta

zxMoveDefence_cornerBlock1827Cont:
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale

zxMoveDefence_cornerBlock2938:
ld      a, (ix+$02)         ; A = valor celda 2
add     a, (ix+$09)         ; A+= valor celda 9
cp      b                   ; ¿A = B?
jr      z, zxMoveDefence_cornerBlock2938Cont ; Sí, mov cruz, bloquear
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$08)         ; A+= valor celda 8
cp      b                   ; ¿A = B?
jr      nz, zxMoveAttack_123 ; No, no mov cruz, salta

zxMoveDefence_cornerBlock2938Cont:
ld      c, KEY6             ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret     z                   ; Si es correcto, sale

Si llegamos hasta aquí, hemos comprobado que el Spectrum no tiene movimiento para ganar, que el jugador uno tampoco, que no corre peligro por movimientos de ataque del jugador uno en diagonal o en cruz; es hora de que el Spectrum pase al ataque.

Esta última parte sólo se ejecutará en los primeros movimientos, según se vaya llenando el tablero estaremos más ocupados en defensa que en ataque.

; -------------------------------------------------------------------
; Movimiento ofensivo horizontal, vertical y diagonal.
; -------------------------------------------------------------------
zxMoveAttack_123:
ld      b, $20              ; B = valor Spectrum con dos casillas
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$02)         ; A+= valor celda 2
add     a, (ix+$03)         ; A+= valor celda 3
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_456 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_456 ; Sí, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 2
call    ToMove              ; Mueve a celda 2
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale

Para el ataque buscamos combinaciones en las que el jugador uno no tenga ninguna celda ocupada, y el Spectrum solo una.

El resto de comprobaciones son muy parecidas a la anterior.

zxMoveAttack_456:
ld      a, (ix+$04)         ; A = valor celda 4
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$06)         ; A+= valor celda 6
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_789 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_789  ; Sí, salta
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret     z                   ; Si es correcto, sale

zxMoveAttack_789:
ld      a, (ix+$07)         ; A = valor celda 7
add     a, (ix+$08)         ; A+= valor celda 8
add     a, (ix+$09)         ; A+= valor celda 9
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_147 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_147  ; Sí, salta
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 8
call    ToMove              ; Mueve a celda 8
ret     z                   ; Si es correcto, sale
inc     c                   ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret     z                   ; Si es correcto, sale

zxMoveAttack_147:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$04)         ; A+= valor celda 4
add     a, (ix+$07)         ; A+= valor celda 7
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_258 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_258  ; Sí, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
ld      c, KEY4             ; C = tecla 4
call    ToMove              ; Mueve a celda 4
ret     z                   ; Si es correcto, sale
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale

zxMoveAttack_258:
ld      a, (ix+$02)         ; A = valor celda 2
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$08)         ; A+= valor celda 8
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_369 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_369  ; Sí, salta
ld      c, KEY2             ; C = tecla 2
call    ToMove              ; Mueve a celda 2
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY8             ; C = tecla 8
call    ToMove              ; Mueve a celda 8
ret     z                   ; Si es correcto, sale

zxMoveAttack_369:
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$06)         ; A+= valor celda 6
add     a, (ix+$09)         ; A+= valor celda 9
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_159 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_159  ; Sí, salta
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale
ld      c, KEY6             ; C = tecla 6
call    ToMove              ; Mueve a celda 6
ret     z                   ; Si es correcto, sale
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret     z                   ; Si es correcto, sale

zxMoveAttack_159:
ld      a, (ix+$01)         ; A = valor celda 1
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$09)         ; A+= valor celda 9
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveAttack_357 ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveAttack_357  ; Sí, salta
ld      c, KEY1             ; C = tecla 1
call    ToMove              ; Mueve a celda 1
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY9             ; C = tecla 9
call    ToMove              ; Mueve a celda 9
ret     z                   ; Si es correcto, sale

zxMoveAttack_357:
ld      a, (ix+$03)         ; A = valor celda 3
add     a, (ix+$05)         ; A+= valor celda 5
add     a, (ix+$07)         ; A+= valor celda 7
ld      c, a                ; Preserva A en C
and     $03                 ; A = casillas jugador 1
jr      nz, zxMoveGeneric   ; ¿Alguna? Sí, salta
ld      a, c                ; Recupera A
and     $30                 ; A = casillas Spectrum
cp      b                   ; ¿Dos ocupadas?
jr      z, zxMoveGeneric    ; Sí, salta
ld      c, KEY3             ; C = tecla 3
call    ToMove              ; Mueve a celda 3
ret     z                   ; Si es correcto, sale
ld      c, KEY5             ; C = tecla 5
call    ToMove              ; Mueve a celda 5
ret     z                   ; Si es correcto, sale
ld      c, KEY7             ; C = tecla 7
call    ToMove              ; Mueve a celda 7
ret     z                   ; Si es correcto, sale

Si llegamos aquí, el Spectrum no ha encontrado dónde mover y mueve a la primera celda que esté libre.

; -------------------------------------------------------------------
; Movimiento genérico. 
; Si con todo lo anterior, no ha hecho movimiento
; mueve a la primera celda libre.
; -------------------------------------------------------------------
zxMoveGeneric:
ld      c, '1'              ; C = ascii 1 (celda 1)
ld      b, $09              ; B = celdas totales
ld      a, $00              ; A = celda vacía
ld      hl, Grid            ; HL = dirección 1ª celda

zxMoveGeneric_loop:
cp      (hl)                ; ¿Celda libre?
jr      z, zxMoveGeneric_end    ; Sí, salta
inc     c                   ; C = ascii siguiente celda
inc     hl                  ; HL = siguiente celda
djnz    zxMoveGeneric_loop  ; Bucle hasta que B = 0

zxMoveGeneric_end:
call    ToMove              ; Mueve a celda libre
ret                         ; Sale

Ya hemos implementado la forma en la que se comporta en las partidas a un jugador el Spectrum. No es perfecta, tiene varias lagunas, y en ocasiones el Spectrum realiza algún movimiento errático. Eso está bien, si lo hiciéramos perfecto no podríamos ganar nunca al Spectrum, y eso no gusta a nadie.

Ahora tenemos que integrar lo que hemos implementado dentro del bucle principal en Main.asm.

Localizamos la etiqueta loop_lostMov, y cinco líneas por debajo sutituimos JR Z, loop_keyCont por:

jr      z, loop_players         ; No activo, salta

Localizamos la etiqueta loop_keyCont y la línea que tenemos por encima, JR loop_cont, la sustituimos por:

jp      loop_cont               ; Salta

Entre esta línea y loop_keyCont vamos añadir el movimiento del Spectrum en el caso de partidas a un jugador, esto hace que JR de un error de fuera de rango, por eso lo hemos sustituido por JP.

Por último, entre esta línea y loop_keyCont añadimos las líneas siguientes:

loop_players:
ld      a, (PlayerMoves)        ; A = jugador que mueve
or      a                       ; ¿Jugador 1?
jr      z, loop_keyCont         ; Sí, salta
ld      a, (MaxPlayers)         ; A = jugadores
cp      $02                     ; ¿2 jugadores?
jr      z, loop_keyCont         ; Sí, salta
call    ZxMove                  ; Mueve Spectrum
jr      nz, loop_players        ; NZ = incorrecto, bucle
push    bc                      ; Preserva BC
ld      bc, SoundSpectrum       ; BC = dirección sonido
call    PlayMusic               ; Emite sonido
pop     bc                      ; Recupera BC
jr      loop_print              ; Pinta movimiento

Obtenemos que jugador mueve, si es el dos obtenemos cuántos jugadores son y si es un solo jugador, mueve el Spectrum.

Compilamos, cargamos en el emulador y vemos el resultado. Si todo ha ido bien ya podemos jugar contra el Spectrum. Si os parece que es demasiado fácil, podéis pulir más los movimientos del Spectrum.

Ensamblador ZX Spectrum, conclusión

Hemos implementado la posibilidad de jugar contra el Spectrum, por lo que ya no es necesario contar con otro contrincante para poder jugar, incluso podéis entrenaros para ganar a otros contrincantes.

En el próximo capítulo acabamos con unos ajustes finales.

Todo el código que hemos generado lo podéis descargar desde aquí.

Ensamblador para ZX Spectrum OXO por Juan Antonio Rubio García.
Esta obra está bajo licencia de Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional License.



Y recuerda, si lo usas no te limites a copiarlo, intenta entenderlo y adaptarlo a tus necesidades.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies

Descubre más desde Espamática

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo