Posted in Programming

## How I made the attack waves to my experimental Amiga game

I’m trying to relax from my studies..

I seem to live very much in the past. Now I would like to share the strange idea I had, when I made the attack waves to my experimental Amiga game. I mean the following:

Well, the asteroids were easy, their y-coordinate is just increased with different steps (3 speeds). The movement to different kind of ships was done in a different way. I drew the paths they were meant to move in a drawing program, Deluxe Paint II.

Then I wrote a C language program that interpreted the curves from Deluxe Paint II’s IFF-pictures. Each (xy) coordinate of the curves was saved to a file and in the game program read from a file.

The problem was, that what to do, if the curve crossed itself? Which direction to follow? I solved this using different colors that told from which direction the ship was coming and some colors told, that which direction not to follow… 🙂

I have the assembly source code to this game here on this blog.

Posted in Mathematics

## Poem on Two

Two shows what even is;
everywhere where it fits evenly,
it exists;
and so is even that,
where two evenly fits.

So many are there twos in a number,
than the relation possible with two evenly is;
if that possible is.

For if it impossible is,
even number not is.

Posted in Philosophy

## Poem on Zero

To eight goes two four times;
To six slips it three times;
To four it fits twice;
To zero it doesn’t fit;
No pairs are there in zero.
No trace of two.
”Where’s my pair?”

Posted in Programming

## The Koch Curve as Snowflake with Python Turtle Graphics

It’s over midnight here.. Since the source code in one of my videos is not very readable, I decided to publish the Python source code of the video in this blog post.

```import turtle

def draw_curve():
window = turtle.Screen()
turt=turtle.Turtle()
turt.setpos(-300,-100)
turt.clear()
turt.speed('fastest')
turt.hideturtle()

vonKoch(turt,5,500)
turt.left(120)
vonKoch(turt,5,500)
turt.left(120)
vonKoch(turt,5,500)
turt.left(120)
vonKoch(turt,5,500)
window.exitonclick()

def vonKoch(turt,depth,length):
if depth == 0: turt.forward(length)
else:
vonKoch(turt,depth-1,length/3)
turt.right(60)
vonKoch(turt,depth-1,length/3)
turt.left(120)
vonKoch(turt,depth-1,length/3)
turt.right(60)
vonKoch(turt,depth-1,length/3)

draw_curve()
```

That’s it for now. Time to go to sleep.

Posted in Programming

## Amiga nostalgia: C language source code to Quest of Love

Quest of Love is an Amiga project that I never finished, but always wanted to create…

The project started as an assembly language project, but it was too much work, so I switched to C language. The video below shows, what I managed to do:

For curiosity and historical — on the other hand educational — reasons, I’ve decided to publish the C language source I have for this project. The assembly version is published in this old blog post..

The comments and names of some variables are in Finnish..

```#include <math.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/displayinfo.h>
#include <graphics/modeid.h>
#include <intuition/intuition.h>
#include <dos/stdio.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <libraries/iff.h>

#define INTUI_V38_NAMES_ONLY

#define LIBVER 39

#define TURN 83

struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;
struct Library *IFFBase=NULL;

struct Screen *screen=NULL;
struct Window *window=NULL;

struct ViewPort *viewport;
struct RastPort *rastport;

ULONG   errorcode;

unsigned char *BM_block=NULL, *BM0, *BM1;
unsigned char *source0, *source1, *source2, *source3, *source4;
unsigned char *dest0, *dest1, *dest2, *dest3, *dest4;

struct BitMap blockbitmap = {2, 1088, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap myBitMap0 = {40, 256, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap myBitMap1 = {40, 256, 0, 5, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
struct BitMap *DrawBitMap=NULL,*ShowBitMap=NULL;

struct Monster {
int x,y;
WORD block;
WORD vital;
WORD exp;
char frame;
char delay;
char elossa;
};

struct Monster Monsters;
struct Monster MonType;

int Blocked(int x, int y, short suunta);
void    DoubleBuffering(short *);
void    DrawMap(UWORD *Map);
void    DrawBlock(int,int,int);
void    Cycle(void);
void  HandleMonsters(void);
void  InitMonsters(void);
void    SetDelay(WORD,WORD);
void  DeAllocateResources();
char  ReadMapFile(char [], UWORD *buffer, int lkm);

int cind, block, k, j;
UWORD colortable;
UWORD copytable;
UWORD *buffer=NULL;
LONG  count;

UWORD *Map = NULL;
UWORD *WorldMap = NULL;
int   map_width, map_heigth;

WORD MapX=0, MapY=0,isoX=0,isoY=0;
char delay=-1;
char turn=TURN;

int main(void) {

short   keepgoing=TRUE;
struct  IntuiMessage *msg;
ULONG       class;
UWORD       code,qualifier;
IFFL_HANDLE iff=NULL;
short       *Which;

if((GfxBase=(struct GfxBase *)
OpenLibrary("graphics.library",LIBVER))==NULL)
{
printf("graphics open failed (V39 kickstart required)\n");
DeAllocateResources();
exit(1);
}

if((IntuitionBase=(struct IntuitionBase *)
OpenLibrary("intuition.library",LIBVER))==NULL)
{
printf("intuition open failed (V39 mkickstart required)\n");
DeAllocateResources();
exit(1);
}

IFFBase=(struct Library*)OpenLibrary(IFFNAME, IFFVERSION);
if (IFFBase == NULL) {
printf("Can not open iff.library\n");
DeAllocateResources();
exit(1);
}

BM_block = (unsigned char *)AllocMem(11520, MEMF_CHIP);
if (!BM_block) {
printf("Memory allocation failed\n");
DeAllocateResources();
exit(1);
}

BM0 = (unsigned char *)AllocMem(51200,MEMF_CHIP|MEMF_CLEAR);
if (!BM0) {
printf("Memory allocation failed\n");
DeAllocateResources();
exit(1);
}

BM1 = (unsigned char *)AllocMem(51200,MEMF_CHIP|MEMF_CLEAR);
if (!BM1) {
printf("Memory allocation failed\n");
DeAllocateResources();
exit(1);
}

buffer=(UWORD *)AllocMem(40, MEMF_CHIP);
if (!buffer) {
printf("Memory allocation failed\n");
DeAllocateResources();
exit(1);
}

InitMonsters();

blockbitmap.Planes=&BM_block;
blockbitmap.Planes=&BM_block;
blockbitmap.Planes=&BM_block;
blockbitmap.Planes=&BM_block;
blockbitmap.Planes=&BM_block;

myBitMap0.Planes=&BM0;
myBitMap0.Planes=&BM0;
myBitMap0.Planes=&BM0;
myBitMap0.Planes=&BM0;
myBitMap0.Planes=&BM0;

myBitMap1.Planes=&BM1;
myBitMap1.Planes=&BM1;
myBitMap1.Planes=&BM1;
myBitMap1.Planes=&BM1;
myBitMap1.Planes=&BM1;

*Which=1;

if((screen=OpenScreenTags(NULL,
SA_Width, 320,
SA_Height, 256,
SA_Depth, 5,
SA_Overscan, FALSE,
SA_AutoScroll, TRUE,
SA_Type, CUSTOMSCREEN|CUSTOMBITMAP,
SA_DisplayID,PAL_MONITOR_ID,
SA_PubName, "My Screen",
SA_BitMap, &myBitMap0,
TAG_END, NULL)
)==NULL)
{
printf("Screen open failed\n");
DeAllocateResources();
exit(1);
}

if((window=OpenWindowTags(NULL,
WA_Left, 0,
WA_Top, 0,
WA_Width, screen->Width,
WA_Height, screen->Height,
WA_IDCMP,IDCMP_VANILLAKEY,
WA_Flags,WFLG_ACTIVATE|WFLG_RMBTRAP|WFLG_BORDERLESS,
WA_ReportMouse, TRUE,
WA_MinWidth,    50,
WA_MinHeight,   20,
WA_MaxWidth,    ~0,
WA_MaxHeight,   ~0,
WA_PubScreen,   screen,
TAG_END, NULL)
)==NULL)
{
printf("Window open failed\n");
DeAllocateResources();
exit(1);
}

viewport=&screen->ViewPort;
rastport=window->RPort;

/* Bittikartoille on aiemmin varattu tila, mikä pitää laskea uudestaan, jos koko muuttuu */

if (!iff) {
printf("IFF file open failed\n");
DeAllocateResources();
exit(1);
}

if (!(IFFL_DecodePic(iff, &blockbitmap))) {
printf("IFF file decoding failed\n");
DeAllocateResources();
exit(1);
}

count = IFFL_GetColorTab(iff, colortable);
IFFL_CloseIFF(iff);

if (!iff) {
printf("IFF file open failed\n");
DeAllocateResources();
exit(1);
}

if (!(IFFL_DecodePic(iff,&myBitMap0))) {
printf("IFF file decoding failed\n");
DeAllocateResources();
exit(1);
}

IFFL_CloseIFF(iff);

for (int i=0; i<4; i++) copytable[i]=colortable[16+i];     BltBitMap(&myBitMap0,0,0,&myBitMap1,0,0,320,256,0xc0,0x1f,&buffer);     source0 = blockbitmap.Planes;     source1 = blockbitmap.Planes;     source2 = blockbitmap.Planes;     source3 = blockbitmap.Planes;     source4 = blockbitmap.Planes;     dest0 = screen->BitMap.Planes+1001;
dest1 = screen->BitMap.Planes+1001;
dest2 = screen->BitMap.Planes+1001;
dest3 = screen->BitMap.Planes+1001;
dest4 = screen->BitMap.Planes+1001;

WorldMap=(UWORD *)AllocMem(9920,MEMF_PUBLIC);
if (WorldMap == NULL) {
printf("Could not allocate memory for WorldMap\n");
DeAllocateResources();
exit(1);
}

DeAllocateResources();
exit(1);
}

Map = WorldMap;
map_width = 80;
map_heigth = 62;

/* -------------------------------------------------------------------------- */

while(keepgoing)
{
WaitTOF();
DoubleBuffering(&Which);
DrawMap(Map);
HandlePlayer();
HandleMonsters();
GeneralRoutines();
Cycle();
/*      printf("Delay %d ; Turn %d\n",delay,turn); */

/*      printf("Blocked %d\n",Blocked(MapX, MapY, 8)); */

while(msg=(struct IntuiMessage *)GetMsg(window->UserPort))
{
class=msg->Class;
code=msg->Code;
qualifier=msg->Qualifier;
/*          printf("%d\n",code); */

switch(class) {
case IDCMP_VANILLAKEY :
switch (code) {
case 27 : /* ESC */
keepgoing=FALSE;
DeAllocateResources();
break;

/***
* Pelaajan ohjaus pääkartalla. Aluksi katsotaan, onko kulkusuunta blokattu;
* jos ei ole ja delay == -1 (eli maastotyypin viive on suoritettu), niin
* liikutaan
* painettuun kulkusuuntaan ja asetetaan uusi delay. Lisäksi tulostetaan
* joko kulkusuunta tai "Slow progress", jos viiveen takia kulkusuuntaan
* ei voinut mennä. Jos tulostettiin "Slow progress", niin delay-arvoa
* vähennetään yhdellä.

***/
case '2' : if (!Blocked(MapX, MapY, 2) && delay<0) {                                             MapY++;                                             isoY++;                                             if (MapY> map_heigth - 1) MapY=0;
SetDelay(MapX,MapY);
Print("South.",6);
} else if (delay>-1) {
Print("Slow progress.",14);
delay--;
}
turn=TURN;
break;

case '8' : if (!Blocked(MapX, MapY, 8) && delay<0) {
MapY--;
isoY--;
if (MapY<0) MapY= map_heigth - 1;                                             SetDelay(MapX,MapY);                                             Print("North.",6);                                      } else if (delay>-1) {
Print("Slow progress.",14);
delay--;
}
turn=TURN;
break;

case '6' : if (!Blocked(MapX, MapY, 6) && delay<0) {                                             MapX++;                                             isoX++;                                             if (MapX> map_width - 1) MapX=0;
SetDelay(MapX,MapY);
Print("East.",5);
} else if (delay>-1) {
Print("Slow progress.",14);
delay--;
}
turn=TURN;
break;

case '4' : if (!Blocked(MapX, MapY, 4) && delay<0) {
MapX--;
isoX--;
if (MapX<0) MapX= map_width - 1;                                             SetDelay(MapX,MapY);                                             Print("West.",5);                                         } else if (delay>-1) {
Print("Slow progress.",14);
delay--;
}
turn=TURN;
break;

case ' ' :  turn=TURN;
Print("Pass.",5);
break;
}
}

}
}
} /* END main */

void DeAllocateResources() {
if (buffer) FreeMem(buffer,40);
/*  if (iff) IFFL_CloseIFF(iff); */
/*  if (BM0) FreeMem(BM0,51200);
if (BM1) FreeMem(BM1,51200); */
if (BM_block) FreeMem(BM_block,11520);
if (window) CloseWindow(window);
if (screen) CloseScreen(screen);
if (BM0) FreeMem(BM0,51200);
if (BM1) FreeMem(BM1,51200);
if (GfxBase) CloseLibrary((struct Library *)GfxBase);
if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
if (IFFBase) CloseLibrary(IFFBase);
if (WorldMap) FreeMem(WorldMap,9920);
}

void HandlePlayer(void) {
DrawBlock(5,6,10);
}

void SetDelay(WORD x,WORD y) {
x+=5; y+=6;
if (x > map_width - 1) x=x - map_width;
if (y > map_heigth - 1) y=y - map_heigth;
/*  printf("%d;%d; %d\n",x,y,Map[x+y*80]); */

switch(Map[x+y*map_width]) {
case 5 : delay=(char)Random(0,4); break;
case 16: delay=(char)Random(0,2); break;
default: delay=-1;
}
/*  printf("%d\n",delay); */
}

void DoubleBuffering(short *Which) {
switch(*Which) {
case 0 : viewport->RasInfo->BitMap=&myBitMap0; *Which=1;
rastport->BitMap=&myBitMap1;
dest0 = myBitMap1.Planes+1001;
dest1 = myBitMap1.Planes+1001;
dest2 = myBitMap1.Planes+1001;
dest3 = myBitMap1.Planes+1001;
dest4 = myBitMap1.Planes+1001;
DrawBitMap=&myBitMap1;
ShowBitMap=&myBitMap0;
break;
case 1 : viewport->RasInfo->BitMap=&myBitMap1; *Which=0;
rastport->BitMap=&myBitMap0;
dest0 = myBitMap0.Planes+1001;
dest1 = myBitMap0.Planes+1001;
dest2 = myBitMap0.Planes+1001;
dest3 = myBitMap0.Planes+1001;
dest4 = myBitMap0.Planes+1001;
DrawBitMap=&myBitMap0;
ShowBitMap=&myBitMap1;
break;
}
ScrollVPort(viewport);
}

/***
* Blocked-rutiini: Parametreina saadaan näkymän vasen yläkulma, mihin lisätään
* arvot, joilla saadan pelaajan sijainti eli näkymän keskikohta. Jos suunnassa,
* johon ollaan menossa on blocki, jonka päälle ei voi mennä palautetaan 1,
* mikä on merkkinä siitä, että hahmoa ei liikuteta, muutoin 0.
***/
int Blocked (int x, int y, short suunta) {
int arvo;

x+=5; y+=6;

if (x > map_width - 1) x=x-map_width;
if (y> map_heigth - 1) y=y- map_heigth;

/*  printf("Block: %d\n", Map[x+y*map_width]); */

switch (suunta) {
case 2 : y+=1; break;
case 8 : y-=1; break;
case 6 : x+=1; break;
case 4 : x-=1; break;
}

/*  printf("Block: %d\n", Map[x+y*map_width]); */

switch (Map[x+y*map_width]) {
case 3 :        arvo = 1; break;
case 8 :        arvo = 1; break;
case 12 :   arvo = 1; break;
case 14 :   arvo = 1; break;
default :   arvo = 0;
}
return arvo;
}

void DrawMap (UWORD *Map) {

/*  printf("DrawMap"); */

int x,y,xg=0,yg=0,xx,yy;

for (y=MapY,yg=0;y<MapY+14;y++,yg++) {
for (x=MapX,xg=0;x<MapX+11;x++,xg++) { /*          printf("xx: %d", xx); */             if (x > map_width - 1) xx=x-map_width; else xx=x;
if (y> map_heigth - 1) yy=y-map_heigth; else yy=y;

if (x > map_width - 1 || y>map_heigth - 1) block = Map[xx+yy*map_width];
else block = Map[x+y*map_width];

block = block << 5;

j=yg*640;
for (int i=block; i<32+block; i++) {
k=xg*2;
dest0[k+j]=source0[i];
i++; k++;
dest0[k+j]=source0[i];
j+=40;
}

j=yg*640;
for (int i=block; i<32+block; i++) {
k=xg*2;
dest1[k+j]=source1[i];
i++; k++;
dest1[k+j]=source1[i];
j+=40;
}

j=yg*640;
for (int i=block; i<32+block; i++) {
k=xg*2;
dest2[k+j]=source2[i];
i++; k++;
dest2[k+j]=source2[i];
j+=40;
}

j=yg*640;
for (int i=block; i<32+block; i++) {
k=xg*2;
dest3[k+j]=source3[i];
i++; k++;
dest3[k+j]=source3[i];
j+=40;
}

j=yg*640;
for (int i=block; i<32+block; i++) {
k=xg*2;
dest4[k+j]=source4[i];
i++; k++;
dest4[k+j]=source4[i];
j+=40;

}

}
}
}

/* Koordinaatit blockeina */

void DrawBlock (int x, int y, int block) {

block = block << 5;             /* kerrotaan 32:lla */

j=y*640;                                /* kerrottaan 40 x 16:a */
for (int i=block; i<32+block; i++) {
k=x*2;
dest0[k+j]=source0[i];
i++; k++;
dest0[k+j]=source0[i];
j+=40;
}

j=y*640;
for (int i=block; i<32+block; i++) {
k=x*2;
dest1[k+j]=source1[i];
i++; k++;
dest1[k+j]=source1[i];
j+=40;
}

j=y*640;
for (int i=block; i<32+block; i++) {
k=x*2;
dest2[k+j]=source2[i];
i++; k++;
dest2[k+j]=source2[i];
j+=40;
}

j=y*640;
for (int i=block; i<32+block; i++) {
k=x*2;
dest3[k+j]=source3[i];
i++; k++;
dest3[k+j]=source3[i];
j+=40;
}

j=y*640;
for (int i=block; i<32+block; i++) {
k=x*2;
dest4[k+j]=source4[i];
i++; k++;
dest4[k+j]=source4[i];
j+=40;
}

}

void Cycle (void) {
int Rs = cind;

for (int i=0; i<4; i++, Rs++) {         if (Rs > 3) Rs=0;
colortable[i+16]=copytable[Rs];
}
cind++;
if (cind > 3) cind = 0;

}

void Print(char teksti[], int pituus) {
ScrollRaster(rastport,0,8,195,119,300,245);
Move(rastport,195,243);
Text(rastport,teksti,pituus);
BltBitMap(DrawBitMap,195,119,ShowBitMap,195,119,105,127,0xc0,0x1f,NULL);
}

char BlockedMonster(WORD x, WORD y, WORD count) {
char i;

for (i=0; i<21; i++) {
if (Monsters[i].x==x && count!=i && Monsters[i].elossa==1)
if (Monsters[i].y==y) return 1;
}

switch (Map[x+y*map_width]) {
case 3 : return 1;
case 8 : return 1;
case 12 : return 1;
case 14 : return 1;
default : return 0;
}

}

void HandleMonsters(void) {
int i,apuX,apuY,RS;

if (turn==TURN) {
for(i=0; i<21; i++) {             if (Monsters[i].elossa) { /* 75% TN:llä lähdetään liikkeelle */                 RS=Random(0,4);                 if (RS>=1) {
apuX=Monsters[i].x;
apuY=Monsters[i].y;
RS=Random(0,2);
if (RS==0) {
if (isoX+5>apuX) apuX++;
else apuX--;
if (!BlockedMonster(apuX, apuY, i)) Monsters[i].x=apuX;
} else if (isoY+6>apuY) apuY++;
else apuY--;
if (!BlockedMonster(apuX,apuY, i)) Monsters[i].y=apuY;
}

}
}
}

/* Laskeskellaan, piirretäänkö monsteria näkyviin */

for(i=0; i<21; i++) {
apuX=Monsters[i].x-isoX;
apuY=Monsters[i].y-isoY;
Monsters[i].frame=Monsters[i].block+Random(0,4);
if (apuX<=10 && apuX>=0)
if (apuY<=13 && apuY>=0) {
/*              printf("%d;%d\n",apuX,apuY); */
DrawBlock(apuX,apuY,Monsters[i].frame);
}
}
}

void InitMonsters (void) {
short type;

MonType.block=64;
MonType.vital=10;
MonType.exp=10;
MonType.delay=0;
MonType.frame=64;

for (int i=0; i<21; i++) {
Monsters[i].x=Random(0,map_width);
Monsters[i].y=Random(0,map_heigth);
switch (Map[Monsters[i].x+Monsters[i].y*map_width]) {
case 3,8,12,14 : Monsters[i].elossa=0; break;
default : Monsters[i].elossa=1;
}

Monsters[i].block=64;
type=Monsters[i].block;
Monsters[i].vital=MonType[type-64].vital;
Monsters[i].exp=MonType[type-64].exp;
Monsters[i].delay=0;
Monsters[i].frame=0;
}

}

void GeneralRoutines() {
turn--;
if (turn<=0) {
delay--;
/*        printf("%d\n",delay); */
if (delay<-1) delay=-1;
turn=TURN;
Print("Pass.",5);
}

}

char ReadMapFile(char nimi[], UWORD *buffer, int koko) {
BPTR file;
LONG length;

/*   printf("%s\n",nimi); */

file=Open(nimi,MODE_OLDFILE);

if (length != koko) {
Close(file);
return 1;
}
return 0;
}

int Random (int alaraja, int ylaraja) {
int Luku;
Luku=rand();
Luku=alaraja+Luku%(ylaraja-alaraja);
return Luku;
}
```
Posted in Mathematics, Programming

## Generating the Cantor’s set with recursion

In my old article I have implementation for Cantor’s set without recursion. Now I implemented the Cantor’s set with recursion in JavaScript. This is better solution.

The definition of the Cantor’s set in the language of set theory is the following: If this doesn’t mean anything to you, you might want to check the old post. 🙂

Below is a picture from the output of the program of this post: Below is the JavaScript listing from Notepad++ in full as png-file: Posted in Programming

## Amiga Nostalgia: The differences between Forbid() and Disable()

For example on Commodore 64 one can use absolute memory addresses somewhat freely; it is sufficient to know the memory map of C64. But on Amiga the case is completely different, because Amiga has multitasking operating system.

As it comes to allocating memory, even if one programs ”hard coded” program, that is: A program that overrides the operation of the operating system and use use directly Amiga’s hardware (Amiga has few co-processors), one can’t assume, that one can ”steal” memory anywhere one wants. In other words one can’t use absolute memory addressing and put one’s data anywhere one wants, because when the user closes the program, the whole multitasking environment can be ”broken”: There can be ”garbage” on the screen or some programs may have crashed, because the ”hard coded” program may have written its own data instead of the data on previously running programs in multitasking environment.

Still, on Amiga there is one absolute memory address, that every programmer in practice must use to get anything done when using the OS, namely address 4. From that memory address one gets so called execbase, that is necessary to call any system routines.

In order to make ”hard coded” program run faster on an Amiga it is practically necessary to stop the operation of the operating system (on an Amiga the OS routines are in ROM, though there are libraries, that are loaded (”opened” by the language of the programmer) to RAM that add more functionality to the OS). There are two ways to do that: To call OS’s Forbid() or Disable().