Monster creation in dungeons
#15
I always get nervous after posts like this. Might I have goofed so that all info in my Guide is wrong??? *gulp*


Some general comments. First an interesting observation:


>Excelent! Time to move on to level 14:
>26.0274% Gold Viper
>26.0274% Succubus

I note that two monsters with different sizes still show up equal ammount of time. This MIGHT not be strange since the difference might not be of importance for this particulary level. I get a difference however.




>The program goes through the array in a series of nested for loops to determine a monster combination. First it
>subtracts a monster size from 3614 (the total size available on one level). It then moves onto another
>monstersize and another until nothing more can spawn for that combination. In order to check to make sure it is
>a unique monster combination it saves the pointers to the mSize (0-9) in a seperate array and compares that to
>the existing array of unique ID.

When I worte my program to calculate the occurance probabilities, I tried several approaches. I wrote it way back on an old computer in Turbo Pascal. One of the problems I had was memory limitations (I never got arround to rewrite it in C now, but I have rewritten the part that output table data, pretty much into spread sheet txt files, like D2, hence my mod question in this forum). Anyway. The options I first tried was to simply calculate all possible combinations. I first set up a recursive method to do it, however, it didn’t go to well since there is really TONS of ways to pick monsters for a level. Especially for church levels that has many small size monsters. Level 15 is easier since it has just a few monsters, most of big size. Next I tried some loop method, again, getting into problems (I don’t recall if it was memory problems or simple time problems). I ended up simply writing a program that did as the game did, pick monsters at random and then loop it many times until the end result did not change by going up in number of attempts. I think 100k times was enough for a level to get a value with one decimal point of precision. So that is how I ended up doing it. I did compare a simple level with the other methods just to compare results, and it did match.

Now, you seem to approach it from the point of view in finding all possible unique combinations, right? Not bad. However, consider that a combination can be reached in multiple ways. Some might be more common than others. Also, from your program, it seems you assume there never spawn more than 3 types of monsters on a level, that is not true. Also, variants with say 4 monsters picked have more possible ways than a combination with 3 monsters (in general I think) and so on. I think that could be it. So you can’t ONLY look at number of unique combinations but also the number of ways you got there (I am bad at these things so can’t tell if all variants with, say, 3 monsters are equal probable, but I think they don’t have to be, since in some of them, there might be ways to go to 4, while for others not and so on. Oh well, someone better at probabilities can probably explain better. Here is the code I wrote for the calculations. Not particulary good looking code but it gets the work done. Note that it uses data tables for monster info and such for which code and info is NOT part of it. I have tried to add comments to explain what is going on. Note that it is Turbo Pascal. I can help converting to C if you need.



Code:
procedure pickpossible(var monsters:mlist;var lastmonster:word;dlvl:word);
var
 dlvl1,dlvl2 : word;
 mslot       : word;
begin
 for mslot:=0 to mlast do begin

This goes through all monsters in the game!


&nbsp; &nbsp;if (mslot<>133) or (dlvl<>24) then begin &nbsp;{ Arch Liches }

Skip these since they won’t end up randomly picked.

&nbsp; &nbsp; &nbsp;dlvl1:=(mdata^[mslot,84]+2) div 2;
&nbsp; &nbsp; &nbsp;dlvl2:=(mdata^[mslot,85]+2) div 2;

mdata is an array with the monster data, at position 84 and 85 for example, you find the dlvl.


&nbsp; &nbsp; &nbsp;if (dlvl>=dlvl1) and (dlvl<=dlvl2) then
&nbsp; &nbsp; &nbsp; &nbsp;if ((adata[mslot] and 128)=0) and ((adata[mslot] and 3)<>0) then begin

Checks if the monster is active in the active list. My program was also meant to handle mods so it read it properly and check for all cases.


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;inc(lastmonster);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;monsters[lastmonster,0]:=mslot;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;monsters[lastmonster,1]:=lastmonster;

Builds a table with possible monsters to pick from on the level we are calculating on.



&nbsp; &nbsp; &nbsp; &nbsp;end;
&nbsp; &nbsp;end;
&nbsp;end;
end; &nbsp;{ pickpossible }

procedure pickrandomskel(var nbr:word);
var
&nbsp;dlvl1,dlvl2 : word;
&nbsp;ok &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: boolean;
&nbsp;tries &nbsp; &nbsp; &nbsp; : word;
begin
&nbsp;ok:=false;
&nbsp;tries:=0;
&nbsp;repeat
&nbsp; &nbsp;nbr:=random(12);
&nbsp; &nbsp;inc(tries);
&nbsp; &nbsp;case nbr of
&nbsp; &nbsp; &nbsp;0.. 3 : nbr:=nbr+8;
&nbsp; &nbsp; &nbsp;4..11 : nbr:=nbr+16;
&nbsp; &nbsp;end;
&nbsp; &nbsp;dlvl1:=(mdata^[nbr,84]+2) div 2;
&nbsp; &nbsp;dlvl2:=(mdata^[nbr,85]+2) div 2;
&nbsp; &nbsp;if (3>=dlvl1) and (3<=dlvl2) then
&nbsp; &nbsp; &nbsp;if ((adata[nbr] and 128)=0) and ((adata[nbr] and 3)<>0) then
&nbsp; &nbsp; &nbsp; &nbsp;ok:=true;
&nbsp;until ok or (tries=1000);
&nbsp;if not ok then
&nbsp; &nbsp;nbr:=maxint;
end; &nbsp;{ pickrandomskel }

Well, on level 3, we want’ a skeleton to go with Leoric. This pick one, it is inserted as a “picked” monster on the level before the random picking starts. Like the game code, my program only check the 12 existing skeleton slots. It do check if they are active though, in case a mod deactivated any!!


procedure seedmonsters(monsters:mlist;lastmonster:word;sizeleft:word;dlvl:word;var picks:plist);
var
&nbsp;run &nbsp; &nbsp; : longint;
&nbsp;minsize : longint;
&nbsp;msize &nbsp; : longint;
&nbsp;size &nbsp; &nbsp;: word;
&nbsp;last &nbsp; &nbsp;: word;
&nbsp;lskel &nbsp; : word;
&nbsp;i,j &nbsp; &nbsp; : word;
&nbsp;monst &nbsp; : mlist;
begin
&nbsp;for run:=1 to runs do begin

As I explained, number of times to do it. I think 100k was enough.



&nbsp; &nbsp;size:=sizeleft;

This is the total size possible on a level. My program actually read the size out of the code since some mods changed it. The size of the golem (from the monster table in case it was changed, should allready be subtracted from the value.



&nbsp; &nbsp;monst:=monsters;
&nbsp; &nbsp;last:=lastmonster;

&nbsp; &nbsp;if dlvl=3 then begin
&nbsp; &nbsp; &nbsp;pickrandomskel(lskel);


For level 3, pick a random skeleton.


&nbsp; &nbsp; &nbsp;for i:=1 to last do begin
&nbsp; &nbsp; &nbsp; &nbsp;if monst[i,0]=lskel then begin

Check if the skeleton picked existed allready as possible monsters for that level. If so, remove it from the list of possible monsters. Note that I shrink this list by simply removing the picked monster and at that place inserting the last monster of the list. Then I shrink the variable that keep track of how many monsters are possible by 1. I keep track for each monster though, how many times it has been picked. Divided by the number of runs, gives the probability of it occuring.


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;size:=size-value(@mdata^[i,4],4);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;inc(picks[monst[i,1]]);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;monst[i]:=monst[last];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dec(last);
&nbsp; &nbsp; &nbsp; &nbsp;end;
&nbsp; &nbsp; &nbsp;end;
&nbsp; &nbsp;end;

&nbsp; &nbsp;repeat
&nbsp; &nbsp; &nbsp;i:=random(last)+1;

Pick one of the remaining possible monsters at random.

&nbsp; &nbsp; &nbsp;msize:=value(@mdata^[monst[i,0],4],4);

Turbo Pascal did not have a good way to cast values, at least not that I used. My “value” function do this. It takes a pointer to the data (in this case in the mdata, column “monst[i,0]”, row 4 (the size). The final 4 indicate what type of value the function should interpret the data as. A 4 should be longint which in that implementation of Turbo Pascal meant a 4 byte integer.


&nbsp; &nbsp; &nbsp;if msize<=size then begin
&nbsp; &nbsp; &nbsp; &nbsp;size:=size-msize;
&nbsp; &nbsp; &nbsp; &nbsp;inc(picks[monst[i,1]]);
&nbsp; &nbsp; &nbsp; &nbsp;monst[i]:=monst[last];
&nbsp; &nbsp; &nbsp; &nbsp;dec(last);
&nbsp; &nbsp; &nbsp;end;

If the random monsters size was small enough to fit. Add it as picked and remove it from the list.



&nbsp; &nbsp; &nbsp;minsize:=maxint;
&nbsp; &nbsp; &nbsp;for j:=1 to last do begin
&nbsp; &nbsp; &nbsp; &nbsp;msize:=value(@mdata^[monst[j,0],4],4);
&nbsp; &nbsp; &nbsp; &nbsp;if msize<minsize then
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;minsize:=msize;
&nbsp; &nbsp; &nbsp;end;

Search remaining monsters to see if any still have a size small enough to fit (one could of course have removed those large enough to not fit, but I did not do that).



&nbsp; &nbsp;until (minsize>size) or (last=0);

Loop until there is no more possible monster to pick.



&nbsp;end;
end; &nbsp;{ seedmonsters }

procedure printlevels;


This procedure (or function if you want to call it that), loops the different dungeon levels.



var
&nbsp;monsters &nbsp; &nbsp; : mlist;
&nbsp;picks &nbsp; &nbsp; &nbsp; &nbsp;: plist;
&nbsp;lastmonster &nbsp;: word;
&nbsp;sizeleft &nbsp; &nbsp; : word;
&nbsp;dlvl,mslot &nbsp; : word;
begin
&nbsp;writeln('Simulating monster selection. Please have patience...');
&nbsp;write('dlvl: ');
&nbsp;writeln(target);
&nbsp;writeln(target,'Monsters occuring on each level. &nbsp;Size: ',totsize,' &nbsp;Runs: ',runs);
&nbsp;writeln(target,'Singleplayer quests may affect the probabilities.');
&nbsp;totsize:=totsize-value(@mdata^[109,4],4); { golem }

Here the golem size is removed.



&nbsp;for dlvl:=1 to lastlevel do begin
&nbsp; &nbsp;writedlvl(output,dlvl,3);
&nbsp; &nbsp;sizeleft:=totsize;
&nbsp; &nbsp;if dlvl=16 then begin
&nbsp; &nbsp; &nbsp;monsters[1,0]:=93;
&nbsp; &nbsp; &nbsp;picks[1]:=runs;
&nbsp; &nbsp; &nbsp;monsters[2,0]:=96;
&nbsp; &nbsp; &nbsp;picks[2]:=runs;
&nbsp; &nbsp; &nbsp;monsters[3,0]:=108;
&nbsp; &nbsp; &nbsp;picks[3]:=runs;
&nbsp; &nbsp; &nbsp;lastmonster:=3; end
&nbsp; &nbsp;else begin

Level 16 is special, so I handle it specially too!!


&nbsp; &nbsp; &nbsp;case dlvl of
&nbsp; &nbsp; &nbsp; &nbsp; 2 : sizeleft:=sizeleft-value(@mdata^[50,4],4); &nbsp; &nbsp;{ Skeleton King }
&nbsp; &nbsp; &nbsp; &nbsp; 3 : sizeleft:=sizeleft-value(@mdata^[51,4],4); &nbsp; &nbsp;{ Butcher &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;18 : sizeleft:=sizeleft-value(@mdata^[117,4],4); &nbsp; { Hork Spawn &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp;19 : begin
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sizeleft:=sizeleft-value(@mdata^[117,4],4); { Hork Spawn &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sizeleft:=sizeleft-value(@mdata^[123,4],4); { Hork Demon &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;
&nbsp; &nbsp; &nbsp; &nbsp;20 : sizeleft:=sizeleft-value(@mdata^[124,4],4); &nbsp; { The Defiler &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;24 : sizeleft:=sizeleft-value(@mdata^[133,4],4); &nbsp; { Arch Lich &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp;end;


Remove the size of monsters that always occur. Note, some monsters that always occur does NOT have its size subtracted if I recall correctly. I only remove those that affect size here.


&nbsp; &nbsp; &nbsp;lastmonster:=0;
&nbsp; &nbsp; &nbsp;pickpossible(monsters,lastmonster,dlvl);

Set up possible monsters to pick from (see above):


&nbsp; &nbsp; &nbsp;for mslot:=1 to lastmonster do
&nbsp; &nbsp; &nbsp; &nbsp;picks[mslot]:=0;

Reset how many times they were picked.


&nbsp; &nbsp; &nbsp;seedmonsters(monsters,lastmonster,sizeleft,dlvl,picks);
&nbsp; &nbsp; &nbsp;case dlvl of
&nbsp; &nbsp; &nbsp; &nbsp;18 : begin
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inc(lastmonster);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; monsters[lastmonster,0]:=117; { Hork Spawn }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; picks[lastmonster]:=runs;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;
&nbsp; &nbsp; &nbsp; &nbsp;19 : begin
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inc(lastmonster);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; monsters[lastmonster,0]:=117; { Hork Spawn }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; picks[lastmonster]:=runs;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;
&nbsp; &nbsp; &nbsp; &nbsp;24 : begin
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inc(lastmonster);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; monsters[lastmonster,0]:=133; { Arch Lich &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; picks[lastmonster]:=runs;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;
&nbsp; &nbsp; &nbsp;end;

This adds a few monsters for picking that would not otherwise have been pickable if I remember correctly. None is in Diablo though, so you might just want to ignore many of these special cases. I think the Arch Liches was especially complicated how they handled it in the code.




&nbsp; &nbsp;end;

&nbsp; &nbsp;writeln(target);
&nbsp; &nbsp;case dlvl of
&nbsp; &nbsp; &nbsp; 1.. 4 : write(target,'Level ',dlvl,' Church');
&nbsp; &nbsp; &nbsp; 5.. 8 : write(target,'Level ',dlvl,' Catacombs');
&nbsp; &nbsp; &nbsp; 9..12 : write(target,'Level ',dlvl,' Caves');
&nbsp; &nbsp; &nbsp;13..16 : write(target,'Level ',dlvl,' Hell');
&nbsp; &nbsp; &nbsp;17..20 : write(target,'Level H',dlvl-16,' The Hive');
&nbsp; &nbsp; &nbsp;21..24 : write(target,'Level C',dlvl-20,' The Crypt');

Just writes the dlvl in a nice way.


&nbsp; &nbsp;end;
&nbsp; &nbsp;writeln(target,',',lastmonster:3,' monsters');
&nbsp; &nbsp;for mslot:=1 to lastmonster do
&nbsp; &nbsp; &nbsp;writeln(target,picks[mslot]/runs*100:5:1,'%',value(@mdata^[monsters[mslot,0],4],4):6,' &nbsp;

Then write out the occurance probability for each monster that was pickable on that level.


',mname^[monsters[mslot,0]]);
&nbsp;end;
&nbsp;writeln;
end; &nbsp;{ printlevels }

procedure dooccurance;
begin
&nbsp;printlevels;
end; &nbsp;{ dooccurance }

Just a forwarding function.

Have fun and if you wonder anything, feel free to ask.
There are three types of people in the world. Those who can count and those who can't.
Reply


Messages In This Thread
Monster creation in dungeons - by the Langolier - 06-22-2003, 10:30 PM
Monster creation in dungeons - by --Pete - 06-23-2003, 01:03 AM
Monster creation in dungeons - by Jane - 06-23-2003, 06:56 AM
Monster creation in dungeons - by the Langolier - 06-23-2003, 07:13 AM
Monster creation in dungeons - by Yogi_Baar - 06-23-2003, 08:05 AM
Monster creation in dungeons - by Yogi_Baar - 06-23-2003, 08:53 AM
Monster creation in dungeons - by dfn - 06-23-2003, 09:09 AM
Monster creation in dungeons - by hell-bringer - 06-23-2003, 05:32 PM
Monster creation in dungeons - by Yogi_Baar - 06-23-2003, 05:59 PM
Monster creation in dungeons - by dfn - 06-24-2003, 06:34 AM
Monster creation in dungeons - by Jarulf - 07-10-2003, 08:01 PM
Monster creation in dungeons - by Zenda - 07-11-2003, 04:42 PM
Monster creation in dungeons - by Jarulf - 07-17-2003, 12:44 PM
Monster creation in dungeons - by the Langolier - 07-22-2003, 09:22 PM
Monster creation in dungeons - by Jarulf - 07-23-2003, 09:38 AM
Monster creation in dungeons - by the Langolier - 07-24-2003, 08:20 AM
Monster creation in dungeons - by Jarulf - 07-24-2003, 11:14 AM
Monster creation in dungeons - by --Pete - 07-24-2003, 04:02 PM
Monster creation in dungeons - by the Langolier - 07-28-2003, 07:29 PM
Monster creation in dungeons - by the Langolier - 07-28-2003, 07:59 PM

Forum Jump:


Users browsing this thread: 7 Guest(s)