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.
Tabla de contenidos
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í.
Enlaces de interés
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.
También puedes visitar el resto de tutoriales:
Y recuerda, si lo usas no te limites a copiarlo, intenta entenderlo y adaptarlo a tus necesidades.