Are you, in fact, a pregnant lady who lives in the apartment next door to Superdeath's parents? - Commodore

Create an account  

 
External AI for MoM?

Hey Jtm!
Welcome back!

Jtm Wrote:I've tried to change code so that Master of Magic calls j_Move_or_attack_reaction subroutine after picking up the coordinates from external ai. This subroutine is used when player moves units with mouse pointer. So far failed. If anyone is interested, I can send the alternative code that implements the call to j_Move_or_attack_reaction.

FYI, you won't be able to make far calls (not easily anyway).
Nor should you try to change existing far calls, without being prepared to do a little more work.

The header of the exe file contains a list of all far calls and adjusts all of them when the exe file is loaded.
If you remove a far call, the corresponding code will still get adjusted.
In effect it would mean you'd have unpredictable code.
So you'd also have to adjust the list in the exe file header.
--I like ILSe
Reply

I like Serena Wrote:FYI, you won't be able to make far calls (not easily anyway).

To be honest I don't understand how and why the 8086 architecture would limit far calls. I suppose I'll have to do my homework then. Sounds like bad news frown Check below. I was planning to use following code but first test was not successful. Will look into it after a good night sleep.

Code:
; Start of Unit_strategy_exe() routine
; WIZARDS.EXE:889E4
[COLOR="YellowGreen"]55        push    bp     ; auto combat
8B EC        mov     bp, sp
83 EC 06    sub     sp, 6
56        push    si_casting_battle_unitID
57        push    di
8B 76 06    mov     si_casting_battle_unitID, [bp+casting_battleUnitID]

8B C6           mov     ax, si_casting_battle_unitID
BA 6E 00        mov     dx, 6Eh ; 'n'
F7 EA           imul    dx
C4 1E 2A 92     les     bx, addr_Battle_unit
03 D8           add     bx, ax
26 8A 47 43     mov     al, es:[bx+43h]
98              cbw
BA 6E 00        mov     dx, 6Eh ; 'n'
F7 EA           imul    dx
C4 1E 2A 92     les     bx, addr_Battle_unit
03 D8           add     bx, ax
26 8B 7F 44     mov     di, es:[bx+struc_Battle_Unit.field_xPos]
26 8B 47 46     mov     ax, es:[bx+struc_Battle_Unit.field_yPos]
89 7E 0A        mov     [bp+target_Y], di
89 46 08    mov    [bp+target_X], ax

A0 97 92    mov    al, [w_kyrub_dseg_9297]
08 C0        or    al, al
74 1C           jz    loc_no_external_AI:
B0 01        mov    al, 1
A2 96 92    mov    [w_kyrub_dseg_9296], al
loc_wait_external_AI:
A0 96 92    mov    al, [w_kyrub_dseg_9296]
08 C0        or    al, al
75 F9           jnz    loc_wait_external_AI
loc_move_or_attack
56        push    si_casting_battle_unitID
9A 34 00 CC 34    call    j_choose_next_unit_for_action
59        pop     cx
FF 36 02 89    push    w_kyrub_dseg_8902
FF 36 00 89    push    w_kyrub_dseg_8900
56        push    si_unit_id
9A 39 00 CC 34    call    j_Move_or_attack_reaction
EB 21        jmp    loc_92548
loc_no_external_AI:

90

56        push    si_casting_battle_unitID
9A 34 00 CC 34    call    j_choose_next_unit_for_action
59        pop     cx[/COLOR]

Code:
Replace opcodes in 0x889E4
[color=DarkOrange]558BEC83EC0656578B76068B7E080BFF754B837E0A0075458BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B7F448BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B474689460A569A3400CC3459893682C5[/color]

with
[color=YellowGreen]558BEC83EC0656578B76068BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B7F44268B4746897E0A894608A0979208C0741CB001A29692A0969208C075F9569A3400CC3459FF360289FF360089569A3900CC34EB2190569A3400CC3459[/color]

I like Serena Wrote:Nor should you try to change existing far calls, without being prepared to do a little more work.

The header of the exe file contains a list of all far calls and adjusts all of them when the exe file is loaded.
If you remove a far call, the corresponding code will still get adjusted.
In effect it would mean you'd have unpredictable code.
So you'd also have to adjust the list in the exe file header.
Ya that wasn't in my mind. Good to know though. Thanks smile

By the way, I've noticed that some of the code is highly NON-optimized. Unit_strategy_exe subroutine has dozens of recalculations of memory address for battle unit. Using one simple variable or register for precalculated value would save hundreds of bytes for other stuff. That's actually the trick I used to make more space in the beginning of the subroutine (compare my code and the original). Removed one 6Eh multiplication etc.
Reply

Interesting.

I tried to interpreter the code you included and I understood nothing or less. alright

Anyway there are AI routines that could be improved, like hunting a unit that has your speed, searching for invisible units, cracking a wall to enter a city and so on.
Only the people crazy enough to think they can change the world of Arcanus and Myrror can do it. rolleye
Reply

I just checked it out.
It works for me too.

I've started MoM and then your program.
When I enter combat, the game remains standing by, as expected.
Then I can specify for each AI unit which unit to attack.
My own units are under my control normally.

When exiting your program, battle resumes as normal.


So we have first step to external AI!
Good! smile
--I like ILSe
Reply

FrancoK Wrote:Anyway there are AI routines that could be improved, like hunting a unit that has your speed, searching for invisible units, cracking a wall to enter a city and so on.

So we need rules, like:

[INDENT]If a unit has wallcrusher, and all enemy units are inside city walls,
then select a piece of wall and go crack it.
[/INDENT]


Or perhaps:

[INDENT]Select a piece of wall and go crack it
IF the unit has an ability to damage a wall, and it's useful to crack the wall.
[/INDENT]

combined with:

[INDENT]It's useful to crack the wall
IF all enemy units are inside walls and all walls are undamaged
[/INDENT]
--I like ILSe
Reply

Jtm Wrote:In addition to saving x and y to 8900 and 8902, the program also changes attacker's battle unit data at +0x43, which is the location for the target unit id.

I've taken a look at the code.

Apparently the function executes the unit strategy that has already been chosen.
This means we can change its current strategy if we want to.

This strategy is mostly determined by battleunit fields +0x43 and +0x54.
If we change them, the unit will do something else.
Code:
+0x43 is the target battleunit id (0-18)
+0x54 is the chosen action (100-109)
    100 melee (if unit has non-zero melee)
    101 unclear??
    102 shoot
    103 unclear??
    104 doom bolt (if unit has the special)
    105 fireball (if unit has the special)
    106 move??
    107 cast spell
    108 cast spell (difference??)
    109 summon demon (if unit has the special)
--I like ILSe
Reply

I like Serena Wrote:So we need rules, like:

[INDENT]If a unit has wallcrusher, and all enemy units are inside city walls,
then select a piece of wall and go crack it.
[/INDENT]


Or perhaps:

[INDENT]Select a piece of wall and go crack it
IF the unit has an ability to damage a wall, and it's useful to crack the wall.
[/INDENT]

combined with:

[INDENT]It's useful to crack the wall
IF all enemy units are inside walls and all walls are undamaged
[/INDENT]
Yes, more or less. :-)

The units able to break a wall are either very powerful (and so better used against units) or weak (like engineers) and need to be protected.

So, the first check is:
There is a wall? If no, skip to the end of the procedure.
There is a defender on the threshold? If no, skip to the end of the procedure.
Is the defender flying? If no, skip to the end of the procedure.
Can I attack it? If yes, chose if attack with all units or some and go to normal attack procedure; if not go to breakwall procedure.

:breakwall procedure
if the breakwaller has ranged attack, attack the closest tower and move the "foot" units to that tower.
if the breakwaller has not ranged attack, move the breakwaller to attack the closest tower and move the "foot" units to that tower, fast/strong units in front, slow/weak in the rear.

:attackFlyingDefender
move the flyingDown unit to attack the flying defender and move the "foot" units close to it, fast/strong units in front, slow/weak in the rear.



This is an example, of course, it can be more simple or more complex.
Only the people crazy enough to think they can change the world of Arcanus and Myrror can do it. rolleye
Reply

I purpose a way to make Ai external.think about external file with external fixed-length commands.
It may look like:take variable from datasegment:adress,make primitive operator ,store it...
Primitive operators may be: load machine code and run it,swap block local variables to file or memory page e.t.s.
Technicaly,you may patch interrupt 3f and patch executable code in memory this way. (The segment relocation in memory have good visibility at this point.) Thus,avoid to corrupt wizards.exe,and make resident program tiny and fast and external-easy programmable. Note,you must be emulator and OS unspecific.
Also,there is bart multiplayer shell,which work right way. (I was try to make combat-extender... it work with this shell also). Dissasemble it is good idea also.but i have no time for this.
Reply

FrancoK Wrote:Yes, more or less. :-)

The units able to break a wall are either very powerful (and so better used against units) or weak (like engineers) and need to be protected.

That's why I put in the condition that all enemies should be within the walls.
Otherwise, it's probably better to simply attack the units outside the wall.


Quote:There is a wall? If no, skip to the end of the procedure.

Let's avoid the word "procedure".
I believe thinking in procedures tends to limit one's view.

Also, let's leave out conditions that have to be implicitly satisfied for the rule to be valid.
If a rule is not valid, for instance due to the lack of city walls, we can simply discard it.
No need to make that explicit.


Quote:There is a defender on the threshold? If no, skip to the end of the procedure.

Whether there's a defender or not at the threshold, I prefer to break the wall if I can.
The reason is that you do not want to enter the city through the threshold.
It will make 4 or more units hit you twice simultaneously, which is very disadvantageous due to the effect of suppression counters.


Quote:Is the defender flying? If no, skip to the end of the procedure.
Can I attack it? If yes, chose if attack with all units or some and go to normal attack procedure; if not go to breakwall procedure.

Same thing.
I think it's better to create options to attack from multiple directions, and also to reduce the defensive bonus of the walls.
FYI, the wall gives a +3 defense bonus, including the threshold.
A broken wall gives a +1 defense bonus at that point (for a unit outside the wall), plus the option to attack.



Quote::breakwall procedure
if the breakwaller has ranged attack, attack the closest tower and move the "foot" units to that tower.
if the breakwaller has not ranged attack, move the breakwaller to attack the closest tower and move the "foot" units to that tower, fast/strong units in front, slow/weak in the rear.

Yes, that sounds good.
When attacking through a broken tower, we have favorable odds.
I'd still also break the wall next to the tower I think.

Quote::attackFlyingDefender
move the flyingDown unit to attack the flying defender and move the "foot" units close to it, fast/strong units in front, slow/weak in the rear.

Can you explain?

Quote:This is an example, of course, it can be more simple or more complex.

I definitely prefer simple.
AI is hard enough to do well.
I want to avoid questionable rules that cause clutter.
Let's start with simple rules that are obviously valid.
Later we can always finetune and make exceptions.
--I like ILSe
Reply

Asfex Wrote:I purpose a way to make Ai external.think about external file with external fixed-length commands.
It may look like:take variable from datasegment:adress,make primitive operator ,store it...
Primitive operators may be: load machine code and run it,swap block local variables to file or memory page e.t.s.
Technicaly,you may patch interrupt 3f and patch executable code in memory this way. (The segment relocation in memory have good visibility at this point.) Thus,avoid to corrupt wizards.exe,and make resident program tiny and fast and external-easy programmable. Note,you must be emulator and OS unspecific.
Also,there is bart multiplayer shell,which work right way. (I was try to make combat-extender... it work with this shell also). Dissasemble it is good idea also.but i have no time for this.

Yes, we need a kind of interface engine, on 2 levels I think.
One (assembly) engine to interact with MoM directly, getting information out and back in.
And a second engine (high level language) to apply high level rules (that "normal" people can understand) to high level data structures.

You appear to have a couple of ideas to refine the assembly engine...
Can you make them more explicit?
--I like ILSe
Reply



Forum Jump: