Thanks for the feedback! Seems, I didn't expect anyone to play it a second time.
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?