C on the Spectrum (z88dk)

The place for codemasters or beginners to talk about programming any language for the Spectrum.
FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Tue Feb 12, 2019 5:09 pm

dfzx wrote:
Tue Feb 12, 2019 9:46 am
Yes, that's the idea! When do we get to play the final game? :)
Ha ha. :) As I still can't get really comfortable with C (just as it always was), I have another idea now.
Please give me some time. I think, it will be really nice. I'll post about it, when it's ready.
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Wed Feb 13, 2019 9:56 pm

This is where I went the last days:

viewtopic.php?f=9&t=1315
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Wed Mar 13, 2019 11:48 am

Hi, back again. ;) I'm trying to rewrite the Connect4-example with Spectrum's Basic now. It would be fast enough to animate the falling tile.
But it's rather slow (too slow), when checking the grid, if anybody has won.

So would it be possible to write a Basic/C-hybrid program? I thought, maybe I could poke the grid to defined memory addresses with Basic, then call a (compiled) C program, also in memory. Write the result of the check by the C program to another memory address, then fall back to Basic. Read the result with Peek there. Is that something, that could be done? Or is it a bad idea?
0 x

User avatar
Einar Saukas
Manic Miner
Posts: 633
Joined: Wed Nov 15, 2017 2:48 pm

Re: C on the Spectrum (z88dk)

Post by Einar Saukas » Wed Mar 13, 2019 1:48 pm

FFoulkes wrote:
Wed Mar 13, 2019 11:48 am
Hi, back again. ;) I'm trying to rewrite the Connect4-example with Spectrum's Basic now. It would be fast enough to animate the falling tile.
But it's rather slow (too slow), when checking the grid, if anybody has won.
It can be done in Sinclair BASIC at a reasonable speed. Take a look:

http://reptonix.awardspace.co.uk/sincla ... t4-pvp.htm

It's available for download here:

https://spectrumcomputing.co.uk/index.p ... 6&id=25224

Notice it should be possible to make it run even faster, since this implementation above was optimized for size instead of speed...
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Wed Mar 13, 2019 7:41 pm

Thanks. Interesting. Don't get how you do it. So you put the grid-data in a one-dimensional array. Does this help by finding diagonal tiles? I'd say, it can't work. But obviously it does. And it's even fast. So it must be ... magic.
0 x

User avatar
Einar Saukas
Manic Miner
Posts: 633
Joined: Wed Nov 15, 2017 2:48 pm

Re: C on the Spectrum (z88dk)

Post by Einar Saukas » Wed Mar 13, 2019 11:59 pm

Actually using a one-dimensional array just helped make a more compact program.

It should be even faster to use separate code for checking each direction.
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Thu Mar 14, 2019 10:42 pm

For now, I stuck to my approach, even if it may feel a bit clumsy now.
First time, I could do something interesting in C. With a BASIC and a Python prototype, writing C becomes less difficult, it seems. In such small programs like on the Spectrum, it may even be ok, to allow oneself a few global variables. Makes it a bit easier and BASIC-like. The speed of such a program in C is quite impressive, though. Finally made the Spectrum agile.

Code: Select all

#include <arch/zx.h>
#include <input.h>
#include <stdio.h>

/*  winwithfour.c, 1.0:

    Compile with: 

    zcc +zx -vn -startup=0 -clib=sdcc_iy -zorg=32768 winwithfour.c -o winwithfour -create-app

    A little game in Python/Pygame (inspired by a very good Z80 Assembly
    tutorial on Youtube).

    Copyright 2019, Forum-name: Major Percival Ffoulkes, GNU GPL v.2,

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

typedef unsigned char byte;

#define BLACK   0
#define BLUE    1
#define RED     2
#define MAGENTA 3
#define GREEN   4
#define CYAN    5
#define YELLOW  6
#define WHITE   7

#define GRIDWIDTH 7
#define GRIDHEIGHT 6
#define MATCHES 4
#define WINCOMBINATIONS 69

struct Grid {
    byte grid[GRIDHEIGHT][GRIDWIDTH];
};

typedef struct Grid Grid;
Grid grid;

struct Combinations {
    byte c[WINCOMBINATIONS][MATCHES];
};

typedef struct Combinations Combinations;
Combinations comb;

byte tpos_x;
int tpos_y;
byte ccol;
byte moves;
byte udg[] = {0, 24, 60, 126, 126, 60, 24, 0};
byte winner;
byte grid_x;
byte grid_y;
int wincombination;
byte outoftiles;

void setColour(byte x, byte y, byte paper, byte ink) {
    byte *p = zx_cxy2aaddr(x, y); 
    *p = 8 * paper + ink;
}

void printCharAt(byte x, byte y, byte c) {
   byte *p;
   byte *dat;
   long int temp;
   byte i;
   temp = 15360 + c * 8;
   dat = (byte *) temp;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = *dat;
      dat++;
      p += 256;
   }
}

void printBlockAt(byte y, byte x, byte paper, byte ink) {
   setColour(x, y, paper, ink);
   byte i;
   byte *p;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = udg[i];
      p += 256;
   }
}

byte my_strlen(byte *mystring) {
    byte x = 0;
    while (*mystring != '\0') {
        x++;
        mystring++;
    }
    return x;
}


byte printAt(byte y, byte x, byte *mystring) {
    byte slen = my_strlen(mystring);
    if (x + slen > 32) {
        puts("Warning: Integer out of range. Nothing printed.");
        printf("x: %d  string: %s\n", x, mystring);
        return 1;
    }
    if (y > 21) {
        puts("Warning: Integer out of range. Nothing printed.");
        printf("y: %d\n", y);
        return 2;
    }
    byte i;
    for (i=0; i < slen; i++) {
        printCharAt(x, y, mystring[i]);
        x++;
    }
    return 0;
}

void initCombinations() {
    byte combination = 0;
    byte row;
    byte column;
    byte i;
    byte u;

    /* Horizontals: */
    for (row=0; row < GRIDHEIGHT; row++) {
        for (column=0; column < GRIDWIDTH + 1 - MATCHES; column++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = (u + column) * 10 + row;
            }
            combination++;
        }
    }

    /* Verticals: */
    for (column=0; column < GRIDWIDTH; column++) {
        for (row=0; row < GRIDHEIGHT + 1 - MATCHES; row++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = column * 10 + u + row;
            }
            combination++;
        }
    }

    /* DiagonalsTopRightToDownLeft. -2 to +3: */
    int d;
    int x;
    int y;
    byte arr[4];
    byte arrcount = 0;
    for (d = - (GRIDHEIGHT - MATCHES); d < GRIDWIDTH + 1 - MATCHES; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = i + u;
                y = i + u;
                if (d > 0) {
                    x += d;
                }
                if (d < 0) {
                    y -= d;
                }
                if (x >= GRIDWIDTH || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }

    /* DiagonalsTopRightToDownLeft: -3 to +2: */
    for (d = - (GRIDWIDTH - MATCHES); d < GRIDHEIGHT - MATCHES + 1; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = GRIDWIDTH - 1 - i - u;
                y = i + u;
                if (d < 0) {
                    x += d;
                }
                if (d > 0) {
                    y += d;
                }
                if (x < 0 || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }
}

void testCombinations() {
/* Print comb.c[][] */
    byte i;
    byte u;
    byte x = 0;
    for (i=0;i < 69;i++) {
        printf("%d: [", x);
        for (u=0;u < MATCHES;u++) {
            printf("%d", comb.c[i][u]);
            if (u < MATCHES - 1) {
                printf(", ");
            }
        }
        printf("]\n");
        x++;
    }
}

void initGrid() {
    byte i; byte u;
    for (i=0;i < GRIDHEIGHT;i++) {
        for (u=0;u < GRIDWIDTH;u++) {
            grid.grid[i][u] = 0;
        }
    }
}

void initGame() {
    winner = 0;
    tpos_x = 3;
    tpos_y = -1;
    ccol = RED;
    moves = 0;
    grid_x = 12;
    grid_y = 6;
    outoftiles = 0;
    wincombination = -1;
    initGrid();
    /* Show Grid: */
    zx_cls(PAPER_WHITE);
    zx_border(INK_RED);
    printAt(grid_y - 3, grid_x - 3, "Win with Four");
    byte y;
    byte x;
    for (y=0; y < GRIDHEIGHT; y++) {
        for (x=0; x < GRIDWIDTH; x++) {
            printBlockAt(grid_y + y, grid_x + x, BLUE, WHITE);
        } 
    }

}

byte getKeyPress() {
    byte c;
    in_wait_key();
    c = in_inkey();
    in_wait_nokey();
    return c;
}

void checkGameWon() {
    byte rc;
    byte yc;
    byte i;
    byte u;
    byte x;
    byte y;
    for (i=0;i< WINCOMBINATIONS;i++) {
        rc = 0;
        yc = 0;
        for (u=0;u< MATCHES;u++) {
            if (comb.c[i][u] >= 10) {
                x = comb.c[i][u] / 10;
            } else {
                x = 0;
            }
            y = comb.c[i][u] - x * 10;
            if (grid.grid[y][x] == RED) {
                rc++;
            }
            if (grid.grid[y][x] == YELLOW) {
                yc++;
            }
            if (rc == MATCHES) {
                winner = RED;
                wincombination = i;
                return;
            }
            if (yc == MATCHES) {
                winner = YELLOW;
                wincombination = i;
                return;
            }
        }
    }
}

void dropTile() {
    if (grid.grid[0][tpos_x] != 0) {
        return;
    }
    byte y;
    int i;
    int count = 0;
    tpos_y = 0;
    for (y=0; y < GRIDHEIGHT; y++) {
        if (grid.grid[y][tpos_x] == 0) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, WHITE);
            tpos_y = y;
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, ccol);
            for (i=0; i<3000;i++){count++;}
        }
    }
    grid.grid[tpos_y][tpos_x] = ccol;
    moves++;
    if (moves >= GRIDWIDTH * GRIDHEIGHT) {
        outoftiles = 1;
        return;
    } 
    checkGameWon();
    if (winner > 0) {
        return;
    }
    tpos_y = -1;
    if (ccol == RED) {
        ccol = YELLOW;
        return;
    }
    if (ccol == YELLOW) {
        ccol = RED;
        return;
    }
}


void showWinner() {
    if (winner == RED) {
        printAt(grid_y + 9, grid_x - 2, "Red has won!");
    }
    if (winner == YELLOW) {
        printAt(grid_y + 9, grid_x - 3, "Yellow has won!");
    }
    byte u;
    byte x;
    byte y;
    for (u=0; u < MATCHES;u++) {
        x = comb.c[wincombination][u] / 10;
        y = comb.c[wincombination][u] - x * 10;
        printBlockAt(grid_y + y, grid_x + x, BLUE, MAGENTA);
    }
}


int main() {
    initCombinations();
    initGame();
    byte key;
    byte running = 1;
    while (running) {
        if (! winner && ! outoftiles) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, ccol);
        }
        key = getKeyPress();
        if (! winner && ! outoftiles) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, WHITE);
            if (key == 'w' && tpos_x < 6) {
                tpos_x++;
            }
            if (key == 'q' && tpos_x > 0) {
                tpos_x--;
            }
            if (key == ' ') {
                dropTile();
            }
        }
        if (key == 'r') {
            initGame();
        }
        if (winner) {
            showWinner();
        }
        if (outoftiles) {
            printAt(grid_y + 9, grid_x - 9, "The Game ended in a Draw!");
        }
        if (winner || outoftiles) {
            printAt(grid_y + 11, grid_x - 8, "Press 'r' to play again.");
        }
        if (key == 'e' || key == 'a') {
            zx_cls(PAPER_WHITE);
            zx_border(INK_WHITE);
            running = 0;
        }
    }
    return 0;
}
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Fri Mar 15, 2019 5:45 pm

Fixed a few bugs, flashing winning tiles and better keyboard I/O (couldn't edit previous posting any more):

Code: Select all

#include <arch/zx.h>
#include <input.h>

/*  winwithfour.c, 1.0:

    Compile with: 

    zcc +zx -vn -startup=0 -clib=sdcc_iy -zorg=32768 winwithfour.c -o winwithfour -create-app

    A little game inspired by a very good Z80 Assembly tutorial on Youtube.

    Copyright 2019, Forum-name: Major Percival Ffoulkes, GNU GPL v.2,

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

typedef unsigned char byte;

#define BLACK   0
#define BLUE    1
#define RED     2
#define MAGENTA 3
#define GREEN   4
#define CYAN    5
#define YELLOW  6
#define WHITE   7

#define GRIDWIDTH 7
#define GRIDHEIGHT 6
#define MATCHES 4
#define WINCOMBINATIONS 69
#define FLASHDELAY 400

#define KEY_q     0
#define KEY_w     1
#define KEY_space 2
#define KEY_r     3
#define KEY_e     4
#define KEY_a     5
#define USEDKEYS  6

struct Grid {
    byte grid[GRIDHEIGHT][GRIDWIDTH];
};

typedef struct Grid Grid;
Grid grid;

struct Combinations {
    byte c[WINCOMBINATIONS][MATCHES];
};

typedef struct Combinations Combinations;
Combinations comb;

byte tpos_x;
int tpos_y;
byte ccol;
byte moves;
byte udg[] = {0, 24, 60, 126, 126, 60, 24, 0};
byte winner;
byte grid_x;
byte grid_y;
int wincombination;
byte outoftiles;
int keys_now[USEDKEYS];
int keys_before[USEDKEYS];
int keys_timeout[USEDKEYS];

void setColour(byte x, byte y, byte paper, byte ink) {
    byte *p = zx_cxy2aaddr(x, y); 
    *p = 8 * paper + ink;
}

void printCharAt(byte x, byte y, byte c) {
   byte *p;
   byte *dat;
   long int temp;
   byte i;
   temp = 15360 + c * 8;
   dat = (byte *) temp;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = *dat;
      dat++;
      p += 256;
   }
}

void printBlockAt(byte y, byte x, byte paper, byte ink) {
   setColour(x, y, paper, ink);
   byte i;
   byte *p;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = udg[i];
      p += 256;
   }
}

byte my_strlen(byte *mystring) {
    byte x = 0;
    while (*mystring != '\0') {
        x++;
        mystring++;
    }
    return x;
}


byte printAt(byte y, byte x, byte *mystring) {
    byte slen = my_strlen(mystring);
    byte i;
    for (i=0; i < slen; i++) {
        printCharAt(x, y, mystring[i]);
        x++;
    }
    return 0;
}

void initCombinations() {
    byte combination = 0;
    byte row;
    byte column;
    byte i;
    byte u;

    /* Horizontals: */
    for (row=0; row < GRIDHEIGHT; row++) {
        for (column=0; column < GRIDWIDTH + 1 - MATCHES; column++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = (u + column) * 10 + row;
            }
            combination++;
        }
    }

    /* Verticals: */
    for (column=0; column < GRIDWIDTH; column++) {
        for (row=0; row < GRIDHEIGHT + 1 - MATCHES; row++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = column * 10 + u + row;
            }
            combination++;
        }
    }

    /* DiagonalsTopRightToDownLeft. -2 to +3: */
    int d;
    int x;
    int y;
    byte arr[4];
    byte arrcount = 0;
    for (d = - (GRIDHEIGHT - MATCHES); d < GRIDWIDTH + 1 - MATCHES; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = i + u;
                y = i + u;
                if (d > 0) {
                    x += d;
                }
                if (d < 0) {
                    y -= d;
                }
                if (x >= GRIDWIDTH || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }

    /* DiagonalsTopRightToDownLeft: -3 to +2: */
    for (d = - (GRIDWIDTH - MATCHES); d < GRIDHEIGHT - MATCHES + 1; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = GRIDWIDTH - 1 - i - u;
                y = i + u;
                if (d < 0) {
                    x += d;
                }
                if (d > 0) {
                    y += d;
                }
                if (x < 0 || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }
}

void testCombinations() {
/* Print comb.c[][] 
    byte i;
    byte u;
    byte x = 0;
    for (i=0;i < 69;i++) {
        printf("%d: [", x);
        for (u=0;u < MATCHES;u++) {
            printf("%d", comb.c[i][u]); 
            if (u < MATCHES - 1) {
                printf(", ");
            }
        }
        printf("]\n");
        x++;
    }
*/
}

void initGrid() {
    byte i; byte u;
    for (i=0;i < GRIDHEIGHT;i++) {
        for (u=0;u < GRIDWIDTH;u++) {
            grid.grid[i][u] = 0;
        }
    }
}

void initGame() {
    byte i;
    winner = 0;
    tpos_x = 3;
    tpos_y = -1;
    ccol = RED;
    moves = 0;
    grid_x = 12;
    grid_y = 6;
    outoftiles = 0;
    wincombination = -1;
    initGrid();
    for (i=0; i < USEDKEYS;i++) {
        keys_timeout[i] = 0;
    }
    /* Show Grid: */
    zx_cls(PAPER_WHITE);
    zx_border(INK_RED);
    printAt(grid_y - 3, grid_x - 3, "Win with Four");
    byte y;
    byte x;
    for (y=0; y < GRIDHEIGHT; y++) {
        for (x=0; x < GRIDWIDTH; x++) {
            printBlockAt(grid_y + y, grid_x + x, BLUE, WHITE);
        } 
    }

}

void checkGameWon() {
    byte rc;
    byte yc;
    byte i;
    byte u;
    byte x;
    byte y;
    for (i=0;i< WINCOMBINATIONS;i++) {
        rc = 0;
        yc = 0;
        for (u=0;u< MATCHES;u++) {
            if (comb.c[i][u] >= 10) {
                x = comb.c[i][u] / 10;
            } else {
                x = 0;
            }
            y = comb.c[i][u] - x * 10;
            if (grid.grid[y][x] == RED) {
                rc++;
            }
            if (grid.grid[y][x] == YELLOW) {
                yc++;
            }
            if (rc == MATCHES) {
                winner = RED;
                wincombination = i;
                return;
            }
            if (yc == MATCHES) {
                winner = YELLOW;
                wincombination = i;
                return;
            }
        }
    }
}

void dropTile() {
    if (grid.grid[0][tpos_x] != 0) {
        return;
    }
    byte y;
    int i;
    int count = 0;
    tpos_y = 0;
    for (y=0; y < GRIDHEIGHT; y++) {
        if (grid.grid[y][tpos_x] == 0) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, WHITE);
            tpos_y = y;
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, ccol);
            for (i=0; i<3000;i++){count++;}
        }
    }
    grid.grid[tpos_y][tpos_x] = ccol;
    moves++;
    if (moves >= GRIDWIDTH * GRIDHEIGHT) {
        outoftiles = 1;
        return;
    } 
    checkGameWon();
    if (winner > 0) {
        return;
    }
    tpos_y = -1;
    if (ccol == RED) {
        ccol = YELLOW;
        return;
    }
    if (ccol == YELLOW) {
        ccol = RED;
        return;
    }
}

void showWinner(byte winner_printed, int flashvar) {
    if (winner_printed == 0) {
        if (winner == RED) {
            printAt(grid_y + 9, grid_x - 2, "Red has won!");
        }
        if (winner == YELLOW) {
            printAt(grid_y + 9, grid_x - 3, "Yellow has won!");
        }
    }
    byte u;
    byte x;
    byte y;
    for (u=0; u < MATCHES;u++) {
        x = comb.c[wincombination][u] / 10;
        y = comb.c[wincombination][u] - x * 10;
        if (winner_printed == 0) {
            printBlockAt(grid_y + y, grid_x + x, BLUE, MAGENTA);
        }
        if (flashvar == 0) {
            setColour(grid_x + x, grid_y + y, MAGENTA, BLUE);
        }
        if (flashvar == FLASHDELAY / 2) {
            setColour(grid_x + x, grid_y + y, BLUE, MAGENTA);
        }
    }
}

void checkKeyboard() {
    byte i;
    for (i=0; i < USEDKEYS;i++) {
        if (keys_before[i] == -1 && keys_now[i] == -1) {
            keys_timeout[i]++;
        }
        if (keys_before[i] == -1 && keys_now[i] == 0) {
            keys_timeout[i] = 0;
        }
        keys_before[i] = keys_now[i];
    };
    keys_now[0] = in_key_pressed(IN_KEY_SCANCODE_q);
    keys_now[1] = in_key_pressed(IN_KEY_SCANCODE_w);
    keys_now[2] = in_key_pressed(IN_KEY_SCANCODE_SPACE);
    keys_now[3] = in_key_pressed(IN_KEY_SCANCODE_r);
    keys_now[4] = in_key_pressed(IN_KEY_SCANCODE_e);
    keys_now[5] = in_key_pressed(IN_KEY_SCANCODE_a);

}

byte checkKey(byte key) {
    if (keys_now[key] == -1 && keys_before[key] == 0) {
        return 1;
    }
    byte i;
    if (keys_now[key] == -1 && keys_timeout[key] > 250) {
        for (i=0;i < USEDKEYS; i++) {
            keys_timeout[i] = 0;
        }
        return 1;
    }
    return 0;
}

void clearTile() {
    printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, WHITE);
}

int main() {
    initCombinations();
    initGame();
    byte running = 1;
    byte winner_printed = 0;
    int flashvar = 0;
    byte draw_printed = 0;
    while (running) {
        checkKeyboard();
        if (! winner && ! outoftiles) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, ccol);
            if (checkKey(KEY_q) && tpos_x > 0) {
                clearTile();
                tpos_x--;
            }
            if (checkKey(KEY_w) && tpos_x < 6) {
                clearTile();
                tpos_x++;
            }
            if (checkKey(KEY_space)) {
                clearTile();
                dropTile();
            }
        }
        if (winner) {
            showWinner(winner_printed, flashvar);
            winner_printed = 1;
            flashvar++;
            if (flashvar == FLASHDELAY) {
                flashvar = 0;
            }
        }
        if (outoftiles && draw_printed == 0) {
            printAt(grid_y + 9, grid_x - 9, "The Game ended in a Draw!");
            draw_printed = 1;
        }
        if (winner || outoftiles) {
            if (winner_printed == 0 || draw_printed == 0) {
                printAt(grid_y + 11, grid_x - 8, "Press 'r' to play again.");
                draw_printed = 1;
            }
        }
        if (checkKey(KEY_r)) {
            initGame();
        }
        if (checkKey(KEY_e) || checkKey(KEY_a)) {
            zx_cls(PAPER_WHITE);
            zx_border(INK_WHITE);
            running = 0;
        }
    }
    return 0;
}
0 x

dfzx
Microbot
Posts: 150
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: C on the Spectrum (z88dk)

Post by dfzx » Fri Mar 15, 2019 6:05 pm

FFoulkes wrote:
Fri Mar 15, 2019 5:45 pm
Fixed a few bugs, flashing winning tiles and better keyboard I/O (couldn't edit previous posting any more):
Yep, that seems to work. :) Unless you play it a second time in which case the winner message doesn't appear. :P

Good to see someone persevering with C. There aren't enough of us!
0 x

FFoulkes
Berk
Posts: 28
Joined: Thu Feb 07, 2019 2:42 pm

Re: C on the Spectrum (z88dk)

Post by FFoulkes » Fri Mar 15, 2019 9:27 pm

dfzx wrote:
Fri Mar 15, 2019 6:05 pm
Yep, that seems to work. :) Unless you play it a second time in which case the winner message doesn't appear. :P
Thanks for the feedback! Seems, I didn't expect anyone to play it a second time. :lol:
As I can't edit the posting above (again), here just for the record once more the fixed version:

Code: Select all

#include <arch/zx.h>
#include <input.h>

/*  winwithfour.c, 1.0:

    Compile with: 

    zcc +zx -vn -startup=0 -clib=sdcc_iy -zorg=32768 winwithfour.c -o winwithfour -create-app

    A little game inspired by a very good Z80 Assembly tutorial on Youtube).

    Copyright 2019, Forum-name: Major Percival Ffoulkes, GNU GPL v.2,

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

typedef unsigned char byte;

enum colours { BLACK, BLUE, RED, MAGENTA, GREEN, CYAN, YELLOW, WHITE };
enum keys { KEY_q, KEY_w, KEY_space, KEY_r, KEY_e, KEY_a };

#define GRIDWIDTH 7
#define GRIDHEIGHT 6
#define MATCHES 4
#define WINCOMBINATIONS 69
#define FLASHDELAY 400
#define USEDKEYS  6

struct Grid {
    byte grid[GRIDHEIGHT][GRIDWIDTH];
};

typedef struct Grid Grid;
Grid grid;

struct Combinations {
    byte c[WINCOMBINATIONS][MATCHES];
};

typedef struct Combinations Combinations;
Combinations comb;

byte tpos_x;
int tpos_y;
byte ccol;
byte moves;
byte udg[] = {0, 24, 60, 126, 126, 60, 24, 0};
byte winner;
byte grid_x;
byte grid_y;
int wincombination;
byte outoftiles;
byte winner_printed;
byte draw_printed;
int keys_now[USEDKEYS];
int keys_before[USEDKEYS];
int keys_timeout[USEDKEYS];

void setColour(byte x, byte y, byte paper, byte ink) {
    byte *p = zx_cxy2aaddr(x, y); 
    *p = 8 * paper + ink;
}

void printCharAt(byte x, byte y, byte c) {
   byte *p;
   byte *dat;
   long int temp;
   byte i;
   temp = 15360 + c * 8;
   dat = (byte *) temp;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = *dat;
      dat++;
      p += 256;
   }
}

void printBlockAt(byte y, byte x, byte paper, byte ink) {
   setColour(x, y, paper, ink);
   byte i;
   byte *p;
   p = zx_cxy2saddr(x, y);
   for (i = 0; i < 8; ++i) {
      *p = udg[i];
      p += 256;
   }
}

byte my_strlen(byte *mystring) {
    byte x = 0;
    while (*mystring != '\0') {
        x++;
        mystring++;
    }
    return x;
}


byte printAt(byte y, byte x, byte *mystring) {
    byte slen = my_strlen(mystring);
    byte i;
    for (i=0; i < slen; i++) {
        printCharAt(x, y, mystring[i]);
        x++;
    }
    return 0;
}

void initCombinations() {
    byte combination = 0;
    byte row;
    byte column;
    byte i;
    byte u;

    /* Horizontals: */
    for (row=0; row < GRIDHEIGHT; row++) {
        for (column=0; column < GRIDWIDTH + 1 - MATCHES; column++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = (u + column) * 10 + row;
            }
            combination++;
        }
    }

    /* Verticals: */
    for (column=0; column < GRIDWIDTH; column++) {
        for (row=0; row < GRIDHEIGHT + 1 - MATCHES; row++) {
            for (u=0; u < MATCHES; u++) {
                comb.c[combination][u] = column * 10 + u + row;
            }
            combination++;
        }
    }

    /* DiagonalsTopRightToDownLeft. -2 to +3: */
    int d;
    int x;
    int y;
    byte arr[4];
    byte arrcount = 0;
    for (d = - (GRIDHEIGHT - MATCHES); d < GRIDWIDTH + 1 - MATCHES; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = i + u;
                y = i + u;
                if (d > 0) {
                    x += d;
                }
                if (d < 0) {
                    y -= d;
                }
                if (x >= GRIDWIDTH || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }

    /* DiagonalsTopRightToDownLeft: -3 to +2: */
    for (d = - (GRIDWIDTH - MATCHES); d < GRIDHEIGHT - MATCHES + 1; d++) {
        for (i=0; i < GRIDWIDTH - MATCHES; i++) {
            arrcount = 0;
            for (u=0; u < MATCHES; u++) {
                x = GRIDWIDTH - 1 - i - u;
                y = i + u;
                if (d < 0) {
                    x += d;
                }
                if (d > 0) {
                    y += d;
                }
                if (x < 0 || y >= GRIDHEIGHT) {
                    break;
                }
                arr[arrcount] = x * 10 + y;
                arrcount++;
            }
            if (arrcount == MATCHES) {
                for (u=0; u < MATCHES; u++) {
                    comb.c[combination][u] = arr[u];
                }
                combination++;
            }
        }
    }
}

void testCombinations() {
/* Print comb.c[][] 
    byte i;
    byte u;
    byte x = 0;
    for (i=0;i < 69;i++) {
        printf("%d: [", x);
        for (u=0;u < MATCHES;u++) {
            printf("%d", comb.c[i][u]); 
            if (u < MATCHES - 1) {
                printf(", ");
            }
        }
        printf("]\n");
        x++;
    }
*/
}

void initGrid() {
    byte i; byte u;
    for (i=0;i < GRIDHEIGHT;i++) {
        for (u=0;u < GRIDWIDTH;u++) {
            grid.grid[i][u] = 0;
        }
    }
}

void initGame() {
    byte i;
    winner = 0;
    tpos_x = 3;
    tpos_y = -1;
    ccol = RED;
    moves = 0;
    grid_x = 12;
    grid_y = 6;
    outoftiles = 0;
    winner_printed = 0;
    draw_printed = 0;
    wincombination = -1;
    initGrid();
    for (i=0; i < USEDKEYS;i++) {
        keys_timeout[i] = 0;
    }
    /* Show Grid: */
    zx_cls(PAPER_WHITE);
    zx_border(INK_RED);
    printAt(grid_y - 3, grid_x - 3, "Win with Four");
    byte y;
    byte x;
    for (y=0; y < GRIDHEIGHT; y++) {
        for (x=0; x < GRIDWIDTH; x++) {
            printBlockAt(grid_y + y, grid_x + x, BLUE, WHITE);
        } 
    }

}

void checkGameWon() {
    byte rc;
    byte yc;
    byte i;
    byte u;
    byte x;
    byte y;
    for (i=0;i< WINCOMBINATIONS;i++) {
        rc = 0;
        yc = 0;
        for (u=0;u< MATCHES;u++) {
            if (comb.c[i][u] >= 10) {
                x = comb.c[i][u] / 10;
            } else {
                x = 0;
            }
            y = comb.c[i][u] - x * 10;
            if (grid.grid[y][x] == RED) {
                rc++;
            }
            if (grid.grid[y][x] == YELLOW) {
                yc++;
            }
            if (rc == MATCHES) {
                winner = RED;
                wincombination = i;
                return;
            }
            if (yc == MATCHES) {
                winner = YELLOW;
                wincombination = i;
                return;
            }
        }
    }
}

void dropTile() {
    if (grid.grid[0][tpos_x] != 0) {
        return;
    }
    byte y;
    int i;
    int count = 0;
    tpos_y = 0;
    for (y=0; y < GRIDHEIGHT; y++) {
        if (grid.grid[y][tpos_x] == 0) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, WHITE);
            tpos_y = y;
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, BLUE, ccol);
            for (i=0; i<3000;i++){count++;}
        }
    }
    grid.grid[tpos_y][tpos_x] = ccol;
    moves++;
    if (moves >= GRIDWIDTH * GRIDHEIGHT) {
        outoftiles = 1;
        return;
    } 
    checkGameWon();
    if (winner > 0) {
        return;
    }
    tpos_y = -1;
    if (ccol == RED) {
        ccol = YELLOW;
        return;
    }
    if (ccol == YELLOW) {
        ccol = RED;
        return;
    }
}

void showWinner() {
    if (winner == RED) {
        printAt(grid_y + 9, grid_x - 2, "Red has won!");
    }
    if (winner == YELLOW) {
        printAt(grid_y + 9, grid_x - 3, "Yellow has won!");
    }
}

void showWinnerTiles(int flashvar) {
    byte u;
    byte x;
    byte y;
    for (u=0; u < MATCHES;u++) {
        x = comb.c[wincombination][u] / 10;
        y = comb.c[wincombination][u] - x * 10;
        if (winner_printed == 0) {
            printBlockAt(grid_y + y, grid_x + x, BLUE, MAGENTA);
        }
        if (flashvar == 0) {
            setColour(grid_x + x, grid_y + y, MAGENTA, BLUE);
        }
        if (flashvar == FLASHDELAY / 2) {
            setColour(grid_x + x, grid_y + y, BLUE, MAGENTA);
        }
    }
}

void checkKeyboard() {
    byte i;
    for (i=0; i < USEDKEYS;i++) {
        if (keys_before[i] == -1 && keys_now[i] == -1) {
            keys_timeout[i]++;
        }
        if (keys_before[i] == -1 && keys_now[i] == 0) {
            keys_timeout[i] = 0;
        }
        keys_before[i] = keys_now[i];
    };
    keys_now[0] = in_key_pressed(IN_KEY_SCANCODE_q);
    keys_now[1] = in_key_pressed(IN_KEY_SCANCODE_w);
    keys_now[2] = in_key_pressed(IN_KEY_SCANCODE_SPACE);
    keys_now[3] = in_key_pressed(IN_KEY_SCANCODE_r);
    keys_now[4] = in_key_pressed(IN_KEY_SCANCODE_e);
    keys_now[5] = in_key_pressed(IN_KEY_SCANCODE_a);

}

byte checkKey(byte key) {
    if (keys_now[key] == -1 && keys_before[key] == 0) {
        return 1;
    }
    byte i;
    if (keys_now[key] == -1 && keys_timeout[key] > 250) {
        for (i=0;i < USEDKEYS; i++) {
            keys_timeout[i] = 0;
        }
        return 1;
    }
    return 0;
}

void clearTile() {
    printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, WHITE);
}

int main() {
    initCombinations();
    initGame();
    byte running = 1;
    int flashvar = 0;
    while (running) {
        checkKeyboard();
        if (! winner && ! outoftiles) {
            printBlockAt(grid_y + tpos_y, grid_x + tpos_x, WHITE, ccol);
            if (checkKey(KEY_q) && tpos_x > 0) {
                clearTile();
                tpos_x--;
            }
            if (checkKey(KEY_w) && tpos_x < 6) {
                clearTile();
                tpos_x++;
            }
            if (checkKey(KEY_space)) {
                clearTile();
                dropTile();
            }
        }
        if (winner && winner_printed == 0) {
            showWinner();
            printAt(grid_y + 11, grid_x - 8, "Press 'r' to play again.");
            winner_printed = 1;
        }

        if (winner) {
            showWinnerTiles(flashvar);
            flashvar++;
            if (flashvar == FLASHDELAY) {
                flashvar = 0;
            }
        }
        if (outoftiles && draw_printed == 0) {
            printAt(grid_y + 9, grid_x - 9, "The Game ended in a Draw!");
            printAt(grid_y + 11, grid_x - 8, "Press 'r' to play again.");
            draw_printed = 1;
        }

        if (checkKey(KEY_r)) {
            initGame();
        }
        if (checkKey(KEY_e) || checkKey(KEY_a)) {
            zx_cls(PAPER_WHITE);
            zx_border(INK_WHITE);
            running = 0;
        }
    }
    return 0;
}
When I tried to change the structs, that just hold two-dimensional arrays to, well, global two-dimensional arrays, the program still worked, but was rather slow. Are structs stored in different memory areas than global arrays?
0 x

Post Reply