; @com.wudsn.ide.asm.mainsourcefile=scorch.asm ; artificial intelligence of tanks goes here! ; in A there is a level of tank's intelligence ; 1-moron, ..., 7-cyborg, 8-UNKNOWN (the best of all) ; at the moment (2003-08-15) I have no idea how ; to program better opponents, but moron is easy - ; - shoots random direction and force ; greeeting to myself 10 years older in 2013-11-09... still no idea ;---------------------------------------------- .proc ArtificialIntelligence ; ; A - skill of the TankNr (in X) ; returns shoot energy and angle in ; ForceTable/L/H and AngleTable ;---------------------------------------------- asl tay :2 dey ;credit KK lda AIRoutines+1,y pha lda AIRoutines,y pha ; it's no necessary - PrepareAIShoot is next proc :) ; jsr PrepareAIShoot ; rts .endp ;---------------------------------------------- .proc PrepareAIShoot ; create low precision table of positions ; by dividing positions by 4 ldy #MaxPlayers-1 loop lda xtankstableL,y sta temp lda xtankstableH,y sta temp+1 ;= /4 :2 lsrw temp lda temp sta LowResDistances,y dey bpl loop ; common values used in AI routines ; address of weapons table (for future use) WepTableToTemp lda TanksWeaponsTableL,x sta temp lda TanksWeaponsTableH,x sta temp+1 rts .endp ;---------------- AIRoutines .word Moron-1 .word Shooter-1 ;Shooter .word Poolshark-1 ;Poolshark .word Tosser-1 ;Tosser .word Chooser-1 ;Chooser .word Spoiler-1 ;Spoiler .word Cyborg-1 ;Cyborg .word Unknown-1 ;Unknown ;---------------------------------------------- .proc Unknown ; random robotank (from Poolshark to Cyborg) randomize 4 13 and #%11111110 tay lda AIRoutines+1,y pha lda AIRoutines,y pha rts .endp ;---------------------------------------------- .proc Moron jsr RandomizeAngle sta NewAngle mwa #80 RandBoundaryLow mwa #800 RandBoundaryHigh jsr RandomizeForce ; choose the best weapon ldy #ind_Buy_me +1 ; if the cheat is active it will fire the BFG :) jmp ChooseBestOffensive.NotFromAll ;rts .endp ;---------------------------------------------- .proc Shooter lda PreviousAngle,x ora PreviousEnergyL,x ora PreviousEnergyH,x beq firstShoot lda PreviousAngle,x cmp #90 bcs shootingLeftAtThisMomentOfTime ; shooting right at this moment of time sec sbc #5 cmp #10 bcs @+ ;not smaller than 10 bcc firstShoot ; GET THE aim againg shootingLeftAtThisMomentOfTime clc adc #5 cmp #170 ; maximum shooter angle bcs firstShoot @ sta NewAngle sec lda PreviousEnergyL,x sbc #5 sta ForceTableL,x lda PreviousEnergyH,x sbc #0 sta ForceTableH,x jmp endo firstShoot ; compare the x position with the middle of the screen lda xTanksTableH,x cmp #>(screenwidth/2) bne @+ lda xTanksTableL,x cmp #<(screenwidth/2) @ bcc tankIsOnTheRight ; enemy tank is on the left randomize 95 125 sta NewAngle bne forceNow tankIsOnTheRight randomize 55 85 sta NewAngle forceNow mwa #100 RandBoundaryLow mwa #800 RandBoundaryHigh ;ldx TankNr ;this is possibly not necessary jsr RandomizeForce endo ;ldx TankNr ;this is possibly not necessary jsr RandomizeForce.LimitForce lda NewAngle sta PreviousAngle,x lda ForceTableL,x sta PreviousEnergyL,x lda ForceTableH,x sta PreviousEnergyH,x ; choose the best weapon jmp ChooseBestOffensive ;rts .endp ;---------------------------------------------- .proc Poolshark jsr UseBatteryOrFlag ; defensives jsr PoolsharkDefensives firstShoot ;find nearest tank neighbour jsr FindBestTarget2 beq EnemyOnLeft ; calculate index to shotangle table ; in temp2 we have x distance divided by 8 lda temp2 :3 lsr @ and #%00000111 clc adc #8 sta AngleTablePointer bne AngleIsSet EnemyOnLeft lda temp2 :3 lsr @ and #%00000111 eor #%00000111 sta AngleTablePointer AngleIsSet randomize 0 8 ldy AngleTablePointer clc adc AngleTable,y sta NewAngle forceNow mwa #300 RandBoundaryLow mwa #700 RandBoundaryHigh ldx TankNr jsr RandomizeForce endo ; choose the best weapon jsr ChooseBestOffensive rts ;---------------------------------------------- AngleTable ; 16 bytes ;ba w $348b L$3350 .by 106,114,122,130,138,146,154,162 .by 18,26,34,43,50,58,66,74 .endp ;---------------------------------------------- .proc UseBatteryOrFlag jsr UseBattery ; as subroutine for reuse in AutoDefense ; if very low energy and no battery then use White Flag lda Energy,x cmp #5 bcs EnoughEnergy ; lower than 5 units - white flag jsr ClearTankNr ; we must hide tank to erase shields (issue #138) lda #ind_White_Flag sta ActiveDefenceWeapon,x jsr PutTankNr ; and draw tank witch Flag EnoughEnergy rts .endp ; .proc UseBattery ; if low energy ten use battery lda Energy,x cmp #30 bcs EnoughEnergy ; lower than 30 units - check battery ldy #ind_Battery lda (temp),y ; has address of TanksWeaponsTable beq NoBatteries ; we have batteries - use one sec sbc #1 sta (temp),y lda #99 sta Energy,x jsr MaxForceCalculate EnoughEnergy NoBatteries rts .endp ;---------------------------------------------- .proc PoolsharkDefensives ; use best defensive :) ; but not allways randomize 1 3 cmp #1 bne NoUseDefensive ; first check check if any is in use lda ActiveDefenceWeapon,x bne DefensiveInUse ldy #last_real_defensive+1 ;the last defensive weapon @ dey cpy #ind_Hovercraft ;first defensive weapon (White Flag, Battery and Hovercraft - never use) beq NoUseDefensive lda (temp),y ; has address of TanksWeaponsTable beq @- ; decrease in inventory sec sbc #1 sta (temp),y ; has address of TanksWeaponsTable ; activate defensive weapon tya ; number of selectet defensive weapon sta ActiveDefenceWeapon,x lda DefensiveEnergy,y sta ShieldEnergy,x NoUseDefensive DefensiveInUse rts .endp ;---------------------------------------------- .proc Tosser jsr UseBatteryOrFlag ; use best defensive :) jsr TosserDefensives ; Toosser is like Poolshark but allways uses defensives jmp Poolshark.firstShoot .endp ;---------------------------------------------- .proc TosserDefensives ; use best defensive :) ; allways ; first check check if any is in use lda ActiveDefenceWeapon,x bne DefensiveInUse ldy #last_real_defensive+1 ;the last defensive weapon @ dey cpy #ind_Hovercraft ;first defensive weapon (White Flag, Battery and Hovercraft - never use) beq NoUseDefensive lda (temp),y ; has address of TanksWeaponsTable beq @- ; decrease in inventory sec sbc #1 sta (temp),y ; has address of TanksWeaponsTable ; activate defensive weapon tya ; number of selectet defensive weapon sta ActiveDefenceWeapon,x lda DefensiveEnergy,y sta ShieldEnergy,x DefensiveInUse NoUseDefensive rts .endp ;---------------------------------------------- .proc Chooser ; like cyborg but more randomizing force jsr UseBatteryOrFlag ; use defensives like Tosser jsr TosserDefensives ; now select best target lda #$00 ; no prefer humans jsr FindBestTarget3 sty TargetTankNr ; aiming jsr TakeAim ; direction still in A (0 - left, >0 - right) ; choose the best weapon jsr ChooseBestOffensive ; randomizing force +-100 sbw Force #100 RandBoundaryLow bpl NotNegativeEnergy mwa #1 RandBoundaryLow NotNegativeEnergy adw Force #100 RandBoundaryHigh jsr RandomizeForce ; if target distance lower than 24 - set weapon to Baby Missile (for security :) jsr GetDistance cmp #6 ; 24/4 bcs HighForce lda #ind_Baby_Missile sta ActiveWeapon,x HighForce rts .endp ;---------------------------------------------- .proc Spoiler ; like cyborg but little randomizing force jsr UseBatteryOrFlag ; use defensives like Tosser jsr TosserDefensives ; now select best target lda #$00 ; no prefer humans jsr FindBestTarget3 sty TargetTankNr ; aiming jsr TakeAim ; direction still in A (0 - left, >0 - right) ; choose the best weapon jsr ChooseBestOffensive ; randomizing force +-50 sbw Force #50 RandBoundaryLow bpl NotNegativeEnergy mwa #1 RandBoundaryLow NotNegativeEnergy adw Force #50 RandBoundaryHigh jsr RandomizeForce ; if target distance lower than 24 - set weapon to Baby Missile (for security :) jsr GetDistance cmp #6 ; 24/4 bcs HighForce lda #ind_Baby_Missile sta ActiveWeapon,x HighForce rts .endp ;---------------------------------------------- .proc Cyborg jsr UseBatteryOrFlag ; use defensives like Tosser jsr TosserDefensives ; now select best target lda #100 ; prefer humans jsr FindBestTarget3 sty TargetTankNr ; aiming jsr TakeAim ; direction still in A (0 - left, >0 - right) ; choose the best weapon ldy #ind_Nuke +1 jsr ChooseBestOffensive.NotFromAll lda Force sta ForceTableL,x lda Force+1 sta ForceTableH,x ; if target distance lower than 32 - set weapon to Baby Missile (for security :) jsr GetDistance cmp #8 ;32/4 bcs HighForce lda #ind_Baby_Missile sta ActiveWeapon,x HighForce rts .endp ;---------------------------------------------- .proc FindBestTarget3 ; find target with lowest energy ; X - shooting tank number ; A - 100 - prefer humans , 0 - equality :) ; returns target tank number in Y and ; direcion of shoot in A (0 - left, >0 - right) ;---------------------------------------------- sta PreferHumansFlag ; jsr MakeLowResDistances lda #202 sta temp2 ; max possible energy lda #0 sta tempor2 ; direction of shoot ;ldx TankNr ldy NumberOfPlayers dey loop01 cpy TankNr beq skipThisPlayer lda eXistenZ,y beq skipThisPlayer lda skilltable,y beq ItIsHuman lda PreferHumansFlag ItIsHuman clc adc Energy,y ; if robotank energy=energy+100 (100 or 0 from PreferHumansFlag) cmp temp2 ; lowest bcs lowestIsLower sta temp2 sty temp2+1 ; number of the closest tank mva #0 tempor2 lda LowResDistances,x cmp LowResDistances,y bcs EnemyOnTheLeft ; enemy on right inc tempor2 ; set direction to right EnemyOnTheLeft lowestIsLower skipThisPlayer dey bpl loop01 ; now we have number of the farthest tank in temp2+1 ; and direction (0 - left, >0 - right) in tempor2 ; let's move them to registers ; in temp2 we have energy of target ldy temp2+1 lda tempor2 rts .endp ;---------------------------------------------- .proc FindBestTarget2 ; find nearest tank neighbour ; X - shooting tank number ; returns target tank number in Y and ; direcion of shoot in A (0 - left, >0 - right) ;---------------------------------------------- ; jsr MakeLowResDistances mva #$ff temp2 ; min possible distance mva #0 tempor2 ; direction of shoot ;ldx TankNr ldy NumberOfPlayers dey loop01 cpy TankNr beq skipThisPlayer lda eXistenZ,y beq skipThisPlayer lda LowResDistances,x cmp LowResDistances,y bcs EnemyOnTheLeft ;enemy on the right sec lda LowResDistances,y sbc LowResDistances,x cmp temp2 ; lowest bcs lowestIsLower sta temp2 sty temp2+1 ; number of the closest tank inc tempor2 ; set direction to right bne lowestIsLower EnemyOnTheLeft sec lda LowResDistances,x sbc LowResDistances,y cmp temp2 ; lowest bcs lowestIsLower sta temp2 sty temp2+1 ; number of the closest tank lowestIsLower skipThisPlayer dey bpl loop01 ; now we have number of the closest tank in temp2+1 ; and direction (0 - left, >0 - right) in tempor2 ; let's move them to registers ; in temp2 we have x distance divided by 8 ldy temp2+1 lda tempor2 rts .endp ;---------------------------------------------- .proc TakeAim ; targeting the tank number TargetTankNr (and Y) ; A (and tempor2) - direction from shooting tank (0 - left, >0 - right) ; returns angle and power of shoot tank X (TankNr) ; in the appropriate variables (Angle and Force) ;---------------------------------------------- lda ActiveWeapon,x pha ; store active weapon mva #$ff SecondTryFlag ; set initial Angle and Force values lda OptionsTable+2 ; selected gravity asl tay ; force correction - lower tank Y position - higher possible force sec lda #screenheight sbc Ytankstable,x sta temp2 clc lda AIForceTable,y sta RandBoundaryLow adc temp2 sta RandBoundaryHigh lda AIForceTable+1,y sta RandBoundaryLow+1 adc #0 sta RandBoundaryHigh+1 jsr RandomizeForce RepeatAim jsr Table2Force ; wind correction 90+(wind/8) mwa Wind temp2 :7 lsrw temp2 clc lda #90 adc temp2 sta NewAngle ; set virtual weapon :) lda #ind_Baby_Missile sta ActiveWeapon,x ; now we have initial valuses mva #%11000000 TestFlightFlag ; check targeting direction lda tempor2 jne AimingLeft AimingRight ; make test Shoot (Flight) jsr SetStartAndFlight lda HitFlag beq NoHitInFirstLoopR ; impossible :) bmi GroundHitInFirstLoopR TankHitInFirstLoopR ; tank hit, but which tank? ; it's our target or not? ldy HitFlag dey cpy TargetTankNr beq EndOfFirstLoopR ; it's our target! ; if it's another tank then check position like ground hit GroundHitInFirstLoopR ; checking only x position of hit ldy TargetTankNr lda xTanksTableH,y cmp XHit+1 bne @+ lda xTanksTableL,y cmp XHit @ bcs HitOnRightSideOfTargetR ; continue targeting clc lda NewAngle adc #5 ; 5 deg to right cmp #(180-20) ; bcs EndOfFirstLoopR ; if angle 180-20 or higher bcs AimSecondTry sta NewAngle jmp AimingRight NoHitInFirstLoopR ; Angle 5 deg to left and end loop sec lda NewAngle sbc #5 sta NewAngle HitOnRightSideOfTargetR dec NewAngle EndOfFirstLoopR mva #5 modify ; set counter (5 turns) SecondLoopR ; make test Shoot (Flight) jsr SetStartAndFlight lda HitFlag beq NoHitInSecondLoopR ; impossible :) bmi GroundHitInSecondLoopR TankHitInSecondLoopR ; tank hit, but which tank? ; it's our target or not? ldy HitFlag dey cpy TargetTankNr beq EndOfSecondLoopR ; it's our target! ; if it's another tank then check position like ground hit GroundHitInSecondLoopR ; checking only x position of hit ldy TargetTankNr lda xTanksTableH,y cmp XHit+1 bne @+ lda xTanksTableL,y cmp XHit @ bcc HitOnLeftSideOfTargetR ; continue targeting dec NewAngle ; 1 deg to left dec modify ; max 5 turns beq EndOfSecondLoopR jmp SecondLoopR HitOnLeftSideOfTargetR ; decrease energy (a little) sbw Force #5 NoHitInSecondLoopR ; Angle 1 deg to right and end loop inc NewAngle EndOfSecondLoopR EndOfAim pla ; and restore active weapon sta ActiveWeapon,x rts AimSecondTry bit SecondTryFlag bpl EndOfAim ; closest RTS inc SecondTryFlag lda #<1000 sta ForceTableL,x lda #>1000 sta ForceTableH,x jsr RandomizeForce.LimitForce jmp RepeatAim AimingLeft ; make test Shoot (Flight) jsr SetStartAndFlight lda HitFlag beq NoHitInFirstLoopL ; impossible :) bmi GroundHitInFirstLoopL TankHitInFirstLoopL ; tank hit, but which tank? ; it's our target or not? ldy HitFlag dey cpy TargetTankNr beq EndOfFirstLoopL ; it's our target! ; if it's another tank then check position like ground hit GroundHitInFirstLoopL ; checking only x position of hit ldy TargetTankNr lda xTanksTableH,y cmp XHit+1 bne @+ lda xTanksTableL,y cmp XHit @ bcc HitOnLeftSideOfTargetL ; continue targeting sec lda NewAngle sbc #5 ; 5 deg to left cmp #21 ; bcc EndOfFirstLoopL ; if angle 20 or lower bcc AimSecondTry sta NewAngle jmp AimingLeft NoHitInFirstLoopL ; Angle 5 deg to right and end loop clc lda NewAngle adc #5 sta NewAngle HitOnLeftSideOfTargetL inc NewAngle EndOfFirstLoopL mva #5 modify ; set counter (5 turns) SecondLoopL ; make test Shoot (Flight) jsr SetStartAndFlight lda HitFlag beq NoHitInSecondLoopL ; impossible :) bmi GroundHitInSecondLoopL TankHitInSecondLoopL ; tank hit, but which tank? ; it's our target or not? ldy HitFlag dey cpy TargetTankNr beq EndOfSecondLoopL ; it's our target! ; if it's another tank then check position like ground hit GroundHitInSecondLoopL ; checking only x position of hit ldy TargetTankNr lda xTanksTableH,y cmp XHit+1 bne @+ lda xTanksTableL,y cmp XHit @ bcs HitOnRightSideOfTargetL ; continue targeting inc NewAngle ; 1 deg to right dec modify ; max 5 turns beq EndOfSecondLoopL jmp SecondLoopL HitOnRightSideOfTargetL ; decrease energy (a little) sbw Force #5 NoHitInSecondLoopL ; Angle 1 deg to left and end loop dec NewAngle EndOfSecondLoopL jmp EndOfAim SetStartAndFlight ; set start point (virtual barrel end :) ) and make test flight ; xtraj+1 and ytraj+1 set clc lda xTanksTableL,x adc #4 sta xtraj+1 lda xTanksTableH,x adc #0 sta xtraj+2 sec lda yTanksTable,x sbc #4 sta ytraj+1 mva #0 ytraj+2 mva NewAngle Angle jsr Flight ldx TankNr rts .endp ;---------------------------------------------- .proc PurchaseAI ; ; A - skill of the TankNr ; makes purchase for AI opponents ; results of this routine are not visible on the screen ;---------------------------------------------- asl tax :2 dex ;credit KK lda PurchaseAIRoutines+1,x pha lda PurchaseAIRoutines,x pha rts .endp ;---------------- PurchaseAIRoutines .word MoronPurchase-1 .word ShooterPurchase-1 ;ShooterPurchase .word PoolsharkPurchase-1 ;PoolsharkPurchase .word TosserPurchase-1 ;TosserPurchase .word TosserPurchase-1 ;ChooserPurchase .word CyborgPurchase-1 ;SpoilerPurchase .word CyborgPurchase-1 ;CyborgPurchase .word TosserPurchase-1 ;UnknownPurchase ;---------------------------------------------- .proc MoronPurchase ;Moron buys nothing rts .endp ;------- .proc TryToPurchaseOnePiece2 ; for Cyborg ; A - weapon number, better it will be in range(1,32) ; TankNr in X ; DOES NOT CHANGE X tay sta temp+1 :3 lsr ; A=A/8 sta temp tya and #%00000111 tay lda bittable,y ldy temp and PurchaseMeTable2,y beq TryToPurchaseOnePiece.SorryNoPurchase jmp TryToPurchaseOnePiece.PurchaseIt .endp ;------- .proc TryToPurchaseOnePiece ; A - weapon number, better it will be in range(1,32) ; TankNr in X ; DOES NOT CHANGE X tay sta temp+1 :3 lsr ; A=A/8 sta temp tya and #%00000111 tay lda bittable,y ldy temp and PurchaseMeTable,y beq SorryNoPurchase PurchaseIt ldy temp+1 lda WeaponPriceL,y sta temp lda WeaponPriceH,y sta temp+1 ;price of the weapon in temp lda MoneyL,x sta temp2 lda MoneyH,x sta temp2+1 ;current monies in temp2 cpw temp2 temp bcc SorryNoPurchase ; deduct monies from the bank account sec lda temp2 sbc temp sta MoneyL,x lda temp2+1 sbc temp+1 sta MoneyH,x lda TanksWeaponsTableL,x sta temp lda TanksWeaponsTableH,x sta temp+1 lda WeaponUnits,y clc adc (temp),y cmp #99 ;max number of weapon units bcc NotExceeded lda #99 NotExceeded sta (temp),y SorryNoPurchase rts .endp ;---------------------------------------------- .proc ShooterPurchase ; first try to buy defensives ; mva #2 tempXroller; number of offensive purchases to perform ldx TankNr @ randomize ind_Battery ind_StrongParachute jsr TryToPurchaseOnePiece ; dec tempXroller ; bne @- ; and now offensives mva #4 tempXroller; number of offensive purchases to perform ;ldx TankNr @ randomize ind_Missile ind_Heavy_Roller jsr TryToPurchaseOnePiece dec tempXroller bne @- rts .endp ;---------------------------------------------- .proc PoolsharkPurchase ; first try to buy defensives ; mva #2 tempXroller; number of offensive purchases to perform ldx TankNr @ randomize ind_Battery ind_Bouncy_Castle jsr TryToPurchaseOnePiece dec tempXroller ; bpl @- ; and now offensives mva #6 tempXroller; number of purchases to perform ;ldx TankNr @ randomize ind_Missile ind_Dirt_Charge jsr TryToPurchaseOnePiece dec tempXroller bne @- rts .endp ;---------------------------------------------- .proc TosserPurchase ; what is my money level ldx TankNr lda MoneyH,x ; money / 256 lsr ; /2 sta tempXroller ; perform this many purchase attempts ; first try to buy defensives ; mva #1 tempXroller; number of defensive purchases to perform @ randomize ind_Battery ind_Bouncy_Castle jsr TryToPurchaseOnePiece dec tempXroller bpl @- ; and now offensives lda MoneyH,x ; money / 256 asl ;*2 sta tempXroller ; perform this many purchase attempts @ randomize ind_Missile ind_Dirt_Charge jsr TryToPurchaseOnePiece dec tempXroller bpl @- rts .endp ;---------------------------------------------- .proc CyborgPurchase ; what is my money level ldx TankNr lda MoneyH,x ; money / 256 lsr ; /2 sta tempXroller ; perform this many purchase attempts ; first try to buy defensives ; mva #1 tempXroller; number of defensive purchases to perform @ randomize ind_Battery ind_Bouncy_Castle jsr TryToPurchaseOnePiece2 dec tempXroller bpl @- ; and now offensives lda MoneyH,x ; money / 256 :3 asl ;*8 sta tempXroller ; perform this many purchase attempts @ randomize first_offensive last_offensive jsr TryToPurchaseOnePiece2 dec tempXroller bpl @- rts .endp ;---------------------------------------------- .proc ChooseBestOffensive ; choose the best weapon ; X - TankNr ;---------------------------------------------- ldy #ind_Dirt_Charge +1 ;the last weapon to choose +1 (not BFG or Laser :) ) NotFromAll ; Y - the last offensive weapon to use + 1 lda TanksWeaponsTableL,x sta temp lda TanksWeaponsTableH,x sta temp+1 loop dey lda (temp),y beq loop tya sta ActiveWeapon,x rts .endp ;---------------------------------------------- .proc GetDistance ; calculates lores ( /4 ) distance from tank X to TargetTankNr(Y) ; result in A ;---------------------------------------------- ldy TargetTankNr lda LowResDistances,x cmp LowResDistances,y @ bcs YisLower sec lda LowResDistances,y sbc LowResDistances,x rts YisLower lda LowResDistances,x sbc LowResDistances,y rts .endp