The following most recent code uses Lua and is a little better designed than the C++ code.
Code that dumps how RNG affects the order of Chance and Community Chest cards.
Index, RNG, order of Chance cards from top to bottom, order of CC cards from top to bottom, final RNG
Chance card values: 0 getoutofjailfree 1 adv to go 2 nearestrail 3 back3space 4 repairs 5 poortax 6 boardwalk 7 illinois 8 adv to stcharles 9 nearestrail 10 bankpays$50 11 nearestutil 12 rideonreading 13 buildingmatures$150 14 gotojail 15 chairman
Community Chest card values: 0 getoutofjailfree 1 inherit$100 2 doctorsfee$50 3 payhospital$100 4 repairs 5 salestock$45 6 schooltax 7 gotojail 8 incometaxrefund$20 9 adv to go 10 receiveservices$25 11 secondprize$10 12 bankerror 13 lifeinsur$100 14 xmasfundmatures$100 15 grandopera
console.clear()
fle=io.open("monopoly_rngvalues.txt","w")
local baserng=0x3E2AD2
local chance={}
local chest={}
local function nextrng(rng,n)
for i=1,n do
local bool1= ((rng%0x400000)>=0x200000)
local bool2= ((rng%0x200000)>=0x100000)
if bool1~=bool2 then
nextbit=1
else
nextbit=0
end
rng=2*rng+nextbit
rng=rng%0x1000000
end
return rng
end
local function xor(a,b)
local pow=1
local res=0
for i=0,7 do
local bool1 = (a%(2*pow)>=pow)
local bool2 = (b%(2*pow)>=pow)
if bool1~=bool2 then
res=res+pow
end
pow=pow*2
end
return res
end
local function getcards(inputrng)
local rng=nextrng(inputrng,1)
for i=0,15 do
chance[i]=-1
chest[i]=-1
end
local cardno=15
local ramf7=0 --chance
while true do
rng=nextrng(rng,3)
local test=rng%256
test=xor(test,ramf7)
test=math.floor(test/16)
if chance[test]~=-1 then
ramf7=ramf7+1
else
chance[test]=cardno
cardno=cardno-1
if cardno<0 then
break
end
end
end
cardno=15
ramf7=16 --chest
while true do
rng=nextrng(rng,3)
local test=rng%256
test=xor(test,ramf7)
test=math.floor(test/16)
if chest[test]~=-1 then
ramf7=ramf7+1
else
chest[test]=cardno
cardno=cardno-1
if cardno<0 then
break
end
end
end
fle:write(string.format("%06X:",inputrng))
fle:write(" Chance: ")
for i=0,15 do
fle:write(string.format("%02d",chance[i])," ")
end
fle:write(" Community_chest: ")
for i=0,15 do
fle:write(string.format("%02d",chest[i])," ")
end
fle:write(string.format(" FinalRNG: %06X",rng),"\n")
end
local prng=baserng
for i=1,5000 do
fle:write(string.format("%04d: ",i-1))
getcards(prng)
prng=nextrng(prng,1)
end
fle:close()
The following code monitors the index of RNG1 and RNG2 when running NES Monopoly in BizHawk.
The code uses a reverse lookup table to give the index for an RNG1 value (up to a max of max_number).
RNG2 is just 35*index mod 256, so the index is calculated just by 139*rng2 mod 256.
console.clear()
local cur_rng=0x3E2AD2
local max_number=10000
local reverse_table={}
local function nextrng(rng,n)
for i=1,n do
local bool1= ((rng%0x400000)>=0x200000)
local bool2= ((rng%0x200000)>=0x100000)
if bool1~=bool2 then
nextbit=1
else
nextbit=0
end
rng=2*rng+nextbit
rng=rng%0x1000000
end
return rng
end
for i=1,max_number do
cur_rng=nextrng(cur_rng,1)
reverse_table[cur_rng]=i
end
console.write("Done")
while true do
local b=mainmemory.read_u24_be(0x37)
local c=mainmemory.read_u8(0x302)
if reverse_table[b] then
gui.text(10,50,"RNG Index = ".. reverse_table[b])
else
gui.text(10,50,"RNG Index = Unknown")
end
gui.text(10,65,"RNG2 Index = ".. (139*c)%256)
emu.frameadvance()
end
The following code is an example of finding the delay frame amounts a,b,c,d,e,f,g,h to manipulate the desired outcome, given an initial RNG1 index.
console.clear()
local baserng=0x3E2AD2
local max_rng_index=100000
local rngtable={}
local mscore=40 --maximum allowable delay
local init_index=820
local init_index2=0
local resultno=0
local floor = math.floor
local function nextrng(rng,n)
for i=1,n do
local bool1= ((rng%0x400000)>=0x200000)
local bool2= ((rng%0x200000)>=0x100000)
if bool1~=bool2 then
nextbit=1
else
nextbit=0
end
rng=2*rng+nextbit
rng=rng%0x1000000
end
return rng
end
local function xor(a,b)
local pow=1
local res=0
for i=0,7 do
local bool1 = (a%(2*pow)>=pow)
local bool2 = (b%(2*pow)>=pow)
if bool1~=bool2 then
res=res+pow
end
pow=pow*2
end
return res
end
--create lookup table for rng values up to max_rng_index
local cur_rng=baserng
for i=1,max_rng_index do
cur_rng=nextrng(cur_rng,1)
rngtable[i]=cur_rng
end
for a=0,mscore do
--console.write("Testing "..tostring(a).."\n")
--emu.frameadvance()
for b=0,mscore-a do
for c=0,mscore-a-b do
local cindex=init_index
local cindex2=init_index2
cindex = cindex + a + (6+b)*8 + (2+c)*16 + floor(((3+b)%4+(2+c))/4)*2 + 27*6 + 5
cindex2 = cindex2 + (6+b) + (2+c)*3 + 27
--first die roll
local die1 = xor(rngtable[cindex]%256,(cindex2*35)%256) % 6 + 1
if die1==6 then
--second die roll
cindex = cindex+5
local die2 = (rngtable[cindex]%256) % 6 + 1
if die2==6 then
cindex = cindex + 103*6 + 85
cindex2 = cindex2 + 103
--next phase
for d=0,mscore-a-b-c do
for e=0,mscore-a-b-c-d do
for f=0,mscore-a-b-c-d-e do
local cindex3=cindex
local cindex4=cindex2
cindex3 = cindex3 + d + (6+e)*6 + (2+f)*16 + floor(((3+e)%4+(2+f))/4)*2 + 27*6 + 5
cindex4 = cindex4 + (6+e) + (2+f)*3 + 27
--third die roll
local die3 = xor(rngtable[cindex3]%256,(cindex4*35)%256) % 6 + 1
if die3==6 or die3==4 then
--fourth die roll
cindex3 = cindex3+5
local die4 = (rngtable[cindex3]%256) % 6 + 1
if die3+die4==10 then
cindex3 = cindex3 + 91*6 + 92
cindex4 = cindex4 + 91
--last phase
for g=0,mscore-a-b-c-d-e-f do
for h=0,mscore-a-b-c-d-e-f-g do
local cindex5=cindex3
local cindex6=cindex4
cindex5 = cindex5 + g + (7+h)*8 + 678 + 8 + 161 + 40*8 + 23*16 + 6*2 + 27*6 + 5
cindex6 = cindex6 + (7+h) + 1 + 40 + 23*3 + 27
--cpu 1st die roll
local die5 = xor(rngtable[cindex5]%256,(cindex6*35)%256) % 6 + 1
if die5==1 then
--cpu 2nd die roll
cindex5 = cindex5+5
local die6 = (rngtable[cindex5]%256) % 6 + 1
if die6==1 then
cindex5 = cindex5 + 43*6 + 1075 + 40*6 + 23*16 + 6*2 + 27*6 + 5
cindex6 = cindex6 + 43 + 40 + 23*3 + 27
--cpu 3rd die roll
local die7 = xor(rngtable[cindex5]%256,(cindex6*35)%256) % 6 + 1
if die7==1 then
--cpu 4th die roll
cindex5 = cindex5+5
local die8 = (rngtable[cindex5]%256) % 6 + 1
if die8==1 then
local fle=io.open("monopoly_result.txt","a")
fle:write(string.format("Delay %d: %d %d %d %d %d %d %d %d\n",a+b+c+d+e+f+g+h,a,b,c,d,e,f,g,h))
fle:close()
resultno=resultno+1
end --if die8
end --if die7
end --if die6
end --if die5
end --end for h
end --end for g
end --if die4
end --if die3
end --end for f
end --end for e
end --end for d
end --if die2
end --if die1
end --end for c
end --end for b
end --end for a
console.write("Done. # results: "..tostring(resultno))
The following code uses C++ and is obsolete.
The following source is for a program used to determine which card combinations are feasible through luck-manipulation. This code is based off an RNG disassembly of the Monopoly ROM.
Most of this stuff is independent of the below project, because it was one of the earlier programs.
Note: Chance deck starts at $0472. Community Chest deck starts at $0482. Both are 16 bytes long.
Chance card values:
0 getoutofjailfree
1 adv to go
2 nearestrail
3 back3space
4 repairs
5 poortax
6 boardwalk
7 illinois
8 adv to stcharles
9 nearestrail
10 bankpays$50
11 nearestutil
12 rideonreading
13 buildingmatures$150
14 gotojail
15 chairman
Community Chest card values:
0 getoutofjailfree
1 inherit$100
2 doctorsfee$50
3 payhospital$100
4 repairs
5 salestock$45
6 schooltax
7 gotojail
8 incometaxrefund$20
9 adv to go
10 receiveservices$25
11 secondprize$10
12 bankerror
13 lifeinsur$100
14 xmasfundmatures$100
15 grandopera
monopoly.cc:
#include <stdio.h>
void rndfunc1(unsigned long *a);
void rndfunc1(unsigned long *a, int x);
void init(char *d);
void printarray(char *d);
void setupcards(unsigned long *a, char *chance, char *commchest);
int main()
{
unsigned long a,b;
char chance[16];
char commchest[16];
int x;
a=0x64EEB2; //starting "random" seed (random as in used by the game's RNG)
x=0;
while(1)
{
b=a;
setupcards(&b,chance,commchest);
//testing for specific cards; change to suit requirements
if(chance[0]==4 && commchest[1]==4 && commchest[0]!=7)
break;
//chance first repairs; comm chest first not gotojail; second repairs
// or for example: if(chance[0]==8 && commchest[0]==15 && chance[1]==3)
// chance first advtostcharles; second back3space; comm chest first grandopera
rndfunc1(&a);
x++;
}
printarray(chance);
printf("\n");
printarray(commchest);
printf("%X\n",a);
printf("Delay: %d",x);
getchar();
return(0);
}
void rndfunc1q(unsigned long *a)
{
long b,c;
char x;
b=*a;
c=b<<1;
c^=b;
if (c&0x200000) x=1; else x=0;
*a=((*a<<1)+x);
}
void rndfunc1(unsigned long *a, int x)
{
for(int i=1;i<=x;i++)
rndfunc1q(a);
}
void init(char *d)
{
for(int i=0;i<=15;i++)
{
d[i]=0xFF;
}
}
void printarray(char *d)
{
for(int i=0;i<=15;i++)
printf("%d\n",d[i]);
}
void setupcards(unsigned long *a, char *chance, char *commchest)
{
unsigned char ramf7, r, cardno;
init(chance);
init(commchest);
rndfunc1q(a);
ramf7=0;
cardno=15;
while(1) //chance //throw random array index from 0 to 15
{
rndfunc1(a,3);
r=*a%256;
r^=ramf7;
r/=16;
if(chance[r]>=0) //spot already occupied
{
ramf7++;
continue;
}
chance[r]=cardno;
if(cardno==0) break;
cardno--;
}
cardno=15;
ramf7=16;
while(1) //commchest
{
rndfunc1(a,3);
r=*a%256;
r^=ramf7;
r/=16;
if(commchest[r]>=0)
{
ramf7++;
continue;
}
commchest[r]=cardno;
if(cardno==0) break;
cardno--;
}
}
This was the code for the main project during Monopoly TASing.
Header only.
monopolyfuncdef.h:
//in monopoly.h
void rndfunc1();
void random1(int x);
void rndfunc2();
void random2(int x);
void random3(int x);
void random4(int x);
void getdice();
void cpumove();
void cpumovedouble();
void playermove(int b, int c);
void playermovedouble(int b, int c);
void finishturn();
void cpumoveab();
//in monopolysolve.cc
void initabc();
void save();
void revert();
void trynext();
void nextabc();
void printdelay();
int calcdelay();
void drreset();
Actually a C file.
Provides functionality
monopoly.h:
#include <stdio.h>
#include "monopolyfuncdef.h"
unsigned long rnda=0;
unsigned char rndb=0;
unsigned char rcnt=0;
unsigned char die1=0;
unsigned char die2=0;
//the linear feedback shift register
//corresponding to polynomial x^22 + x^21 + 1 in Z_2
void rndfunc1()
{
long b,c;
char x;
b=rnda;
c=b<<1;
c^=b;
if (c&0x200000) x=1; else x=0;
rnda=((rnda<<1)+x);
}
void random1(int x)
{
for(int i=1;i<=x;i++)
rndfunc1();
}
void rndfunc2()
{
random1(5);
rndb+=35;
}
//reset rcnt before doing this
void random2(int x)
{
for(int i=1;i<=x;i++)
{
random1(3);
rndfunc2();
rcnt++;
}
}
//don't forget to rcnt++ between random2 and random3
void random3(int x)
{
for(int i=1;i<=x;i++)
{
rndfunc1();
rndfunc2();
rndfunc2();
rndfunc2();
rcnt++;
if(rcnt%4==0)
random1(2);
}
}
void random4(int x)
{
for(int i=1;i<=x;i++)
{
rndfunc1();
rndfunc2();
rcnt++;
}
}
void getdice()
{
unsigned char c;
random1(5);
c=rnda%256;
c^=rndb;
c%=6;
c++;
die2=c;
random1(5);
c=rnda%256;
c%=6;
c++;
die1=c;
}
void cpumove()
{
rcnt=0;
random2(40);
rcnt++;
random3(23);
random4(27);
getdice();
}
//somehow uses random4 this time
void cpumovedouble()
{
rcnt=0;
random4(40);
rcnt++;
random3(23);
random4(27);
getdice();
}
//"glitched" code; not used
void cpumoveab()
{
rcnt=0;
random4(1);
random2(39);
rcnt++;
random3(23);
random4(27);
getdice();
}
void playermove(int b, int c)
{
rcnt=0;
random2(6+b);
rcnt++;
random3(2+c);
random4(27);
getdice();
}
//uses random4
void playermovedouble(int b, int c)
{
rcnt=0;
random4(6+b);
rcnt++;
random3(2+c);
random4(27);
getdice();
}
void finishturn()
{
random4(31+(die1+die2)*6);
}
This is a program used to find input delay that produces the correct rolls, subject to 3 degrees of freedom per turn over multiple turns. Originally used to find optimal input for a game, this program has now been abused (in the //quickie section) to find input for one or two turns.
monopolysolve.cc:
#include "monopoly.h"
int a,b,c,ptr=1;
unsigned long rar[40];
unsigned char rbr[10];
int dr[10][3];
int f=0;
//int x[10]={0,5,20,10,150,0}; //delay 31
int x[10]={0,10,43,43,43,0};
unsigned long debug, debug3;
unsigned char debug2, debug4;
long rerecords=0;
char reverttag=0;
int delay=1000;
int ff;
int fg;
int main()
{
//rnda=0x4BFFE3; //first strat
//rnda=0x41D723; //ollie strat
rnda=0x07D580; //starting seeds
rndb=0x28; //starting seeds
rar[1]=rnda;
rbr[1]=rndb;
initabc();
drreset();
while (1)
{
switch(ptr)
{
case 1: //nd 6 to oriental
//random1(3+a);
a=-1;
playermove(b,c);
//quickie
if(die2+die1==2 && die1==1) //to get 1+1
{printf("%d %d %d", a,b,c); goto br;}
trynext();
break;
/* a=-1;
random2(1+b);
random1(32+c);
fg=rndb;
cpumove();
if(die1==die2 && die1!=2); //to get any double except 2+2
else {trynext(); break;}
ff=die1;
finishturn();
if(ff==6 || ff==5) random1(114); else if (ff==4) random1(120); else random1(116); //delays for whichever doubles
cpumovedouble();
if(die1+die2+2*ff==12 && die1!=die2)
{printf("%d %d %d %d %d %d %X", a,b,c, ff, die1, die2, fg); goto br;}
trynext();
break;*/
//\quickie
if(die1+die2==6 && die1!=3)
{
finishturn();
debug=rnda;
save();
initabc();
a=-1;
b=-1;
}
else
{
trynext();
}
break;
case 2: //cpu nd 8 to vermont
random1(85+c);
cpumove();
if(die1+die2==8 && die1!=4)
{
finishturn();
random1(118);
save();
initabc();
a=-1;
}
else
{
trynext();
}
break;
case 3: //3 to connecticut
playermove(b,c);
if(die1+die2==3)
{
finishturn();
//strategy#1 random1(492+350-5);
//random1(524+350-5);
random1(85);
random2(7);
debug3=rnda;
debug4=rndb;
/*2 prop mortgage random1(542);*/
/*1 prop mortgage */ random1(528+8);
//0 prop mortgage random1(524);
//random1(184);
save();
initabc();
a=-1;
}
else
{
trynext();
}
break;
case 4: //cpu roll double
//goto solution;
random2(b);
random1(c);
debug=rnda;
debug2=rndb;
random4(1);
//strategy#1
////random1(1801);
//random1(2279);
//random1(2109);
//strategy#2
//random1(938);
random1(1250);
//random1(2275);
cpumove();
if(die1!=die2)
{
trynext();
break;
}
finishturn();
f=die1;
if(f==1 || f==6) random1(120); else random1(114);
cpumovedouble();
if(die1!=7-f || die2!=7-f)
{
trynext();
break;
}
finishturn();
if(f==1 || f==6) /*random1(987);*/ /*random1(983);*/ random1(234); else{ trynext();break;}//random1(1299);
cpumovedouble();
if(die1+die2==11) //solution
{
save();
if(calcdelay()<=delay)
{
delay=calcdelay();
//solution:
printdelay();
}
//goto br;
revert();
}
else trynext();
}
reverttag=0;
if(ptr==0) break;
//if(dr[1][0]>=1) break;
//printf("%d, %d",die1,die2);
}
br:
printf("\n%d rerecords\n", rerecords);
printf("done");
getchar();
return 0;
}
void initabc()
{
a=0;
b=0;
c=0;
}
void save()
{
dr[ptr][0]=a;
dr[ptr][1]=b;
dr[ptr][2]=c;
ptr++;
rar[ptr]=rnda;
rbr[ptr]=rndb;
}
void revert()
{
//printf("sdf");
/* dr[ptr][0]=0;
dr[ptr][1]=0;
dr[ptr][2]=0;*/
reverttag=1;
ptr--;
if(ptr!=0)
{
a=dr[ptr][0];
b=dr[ptr][1];
c=dr[ptr][2];
trynext();
}
}
void trynext()
{
if(reverttag==0) rerecords++;
rnda=rar[ptr];
rndb=rbr[ptr];
nextabc();
}
void nextabc()
{
if(c>=x[ptr] || calcdelay()>delay)
if(b>=x[ptr] || b==-1 || calcdelay()-c>delay)
if(a>=x[ptr] || a==-1 || calcdelay()-c-b>delay)
revert();
else{c=0; b=0; a++;}
else{c=0; b++;}
else c++;
/* dr[ptr][0]=a;
dr[ptr][0]=b;
dr[ptr][0]=c;*/
// if(calcdelay()>delay) revert();
}
void printdelay()
{
for(int i=1;i<=4;i++)
{
for(int j=0;j<=2;j++)
if(dr[i][j]==-1) printf("x "); else printf("%d ", dr[i][j]);
printf("%X %X\n",rar[i]&0xFFFFFF,rbr[i]);
//if(i==1) printf("%X %X ", debug3&0xFFFFFF, debug4);
}
printf("%d %X %X %d\n\n", f,debug&0xFFFFFF,debug2,delay);
}
void drreset()
{
for(int i=1;i<=4;i++)
for(int j=0;j<=2;j++)
dr[i][j]=0;
}
int calcdelay()
{
int t=0;
for(int i=1;i<=ptr-1;i++)
for(int j=0;j<=2;j++)
if(dr[i][j]!=-1)
t+=dr[i][j];
if(ptr!=5)
{
if(a!=-1) t+=a; if(b!=-1) t+=b; if(c!=-1) t+=c;
}
return t;
}
This program searches for endings. Meaning that it searches for a random seed that generates automatic consecutive CPU losses after end of input.
Where the CPU players are is not important a priori. The important part is that a CPU rolls a double into an orange hotel group (St. James), and then 2 to Tennessee or 3 to New York. It is permitted for one CPU (at most) to go from either of these three to the next Chance, since the card is (manipulated to be) go back 3 spaces onto New York.
In other words each CPU must roll a double first, then 2 or 3 second, with at most one exception permitted to roll 4 or 6 second.
The program only searches over the first few
RndVal1 (rnda)
random values for all 256 values of RndVal2 (rndb)
. The period of RndVal1
is 2^22-1, or over 4 million, so most values of RndVal1
are impractical.
For manipulating 3 CPUs (6 rolls), there are many short-range solutions. For 4 CPUs (8 rolls), there are very few short-range solutions. No solution has been found for 5 CPUs (10 rolls) within a range of 100000
RndVal1
iterations.
There are two timing versions, one with hurry timing and one with relax timing.
monopolysolve2.cc:
#include "monopoly.h"
#define BEGINSEED 0x3E2AD2
#define SEARCHOFFSET 9000
#define LIMIT 20000
#define PLAYERS 2
//#define HURRY
#ifdef HURRY
unsigned int billtime=120;
#else
unsigned int billtime=464;
#endif
unsigned long c=BEGINSEED,cc,cc2,delay=0;
unsigned int i,f,f2,g,g2,h,h2,j,j2,k,k2,bb,bb2;
unsigned int cnt=0;
int main()
{
//rnda=BEGINSEED;
rnda=BEGINSEED;
random1(SEARCHOFFSET);
c=rnda;
rnda=c;
rndb=0;
rcnt=0;
random2(40);
rcnt++;
random3(23);
random4(27);
cc=rnda;
bb=rndb;
random1(10);
random4(31);
random1(billtime);
rcnt=0;
random4(40);
rcnt++;
random3(23);
random4(27);
cc2=rnda;
bb2=rndb;
while(delay<=LIMIT)
{
delay++;
rnda=c;
random1(1);
c=rnda;
rnda=cc;
random1(1);
cc=rnda;
rnda=cc2;
random1(1);
cc2=rnda;
for(i=0;i<=255;i++)
{
cnt=0;
rnda=cc;
rndb=bb;
rndb+=i;
getdice();
if(die1!=die2) continue;
f=die1;
rnda=cc2;
rndb=bb2;
rndb+=i;
random4((die1+die2)*6);
getdice();
if(die1+die2!=3 && die1+die2!=2 && die1+die2!=4 && die1+die2!=6) continue;
f2=die1+die2;
if(f2==4 || f2==6){ cnt++; random1(114);}
//if(die1+die2==2 && f==3) continue;
finishturn();
random1(439);
cpumove();
if(die1!=die2) continue;
finishturn();
g=die1;
random1(billtime);
cpumovedouble();
if(die1+die2!=3 && die1+die2!=2 && die1+die2!=4 && die1+die2!=6) continue;
g2=die1+die2;
if(g2==4 || g2==6){ cnt++; random1(114);}
//if(die1+die2==2 && g==3) continue;
finishturn();
random1(439);
cpumove();
if(die1!=die2) continue;
finishturn();
h=die1;
random1(billtime);
cpumovedouble();
if(die1+die2!=3 && die1+die2!=2 && die1+die2!=4 && die1+die2!=6) continue;
h2=die1+die2;
if(h2==4 || h2==6){ cnt++; random1(114);}
//if(die1+die2==2 && h==3) continue;
finishturn();
random1(439);
cpumove();
if(die1!=die2) continue;
finishturn();
j=die1;
random1(billtime);
cpumovedouble();
if(die1+die2!=3 && die1+die2!=2 && die1+die2!=4 && die1+die2!=6) continue;
j2=die1+die2;
if(j2==4 || j2==6){ cnt++; random1(114);}
//if(die1+die2==2 && h==3) continue;
finishturn();
random1(439);
/*
cpumove();
if(die1!=die2) continue;
finishturn();
k=die1;
random1(billtime);
cpumovedouble();
if(die1+die2!=3 && die1+die2!=2 && die1+die2!=4 && die1+die2!=6) continue;
k2=die1+die2;
if(k2==4 || k2==6){ cnt++; random1(114);}
//if(die1+die2==2 && h==3) continue;
finishturn();
random1(439);
*/
if(cnt>=2) continue;
printf("%d%d %d%d %d%d %d%d %d%d %X %X %d\n",f,f2,g,g2,h,h2,j,j2,k,k2,c&0xFFFFFF,i,delay);
}
if(delay%5000==0)
printf("Done delay %d\n",delay);
}
printf("done");
getchar();
return 0;
}