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.
It can be done in Sinclair BASIC at a reasonable speed. Take a look:FFoulkes wrote: ↑Wed Mar 13, 2019 11:48 amHi, 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.
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;
}
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;
}
Yep, that seems to work. Unless you play it a second time in which case the winner message doesn't appear.
Thanks for the feedback! Seems, I didn't expect anyone to play it a second time.
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;
}
No, C is much simpler than BASIC or any other high level language. There's no concept of faster or slower memory areas (other than the Spectrum's concept of contended memory as imposed by hardware, irrelevant in this example because you're only using 0x8000 upwards which is outside the contended range).
Code: Select all
-SO3 --max-allocs-per-node200000
I think there might be a bit of room for optimisation in his implementation. The code isn't easy to read but as far as I can see he's pre-populating an array with the 69 winning patterns then doing a comparison of the current board against that data set each move. So, perhaps not as advanced as what's in your head, but a decent exercise for a C beginner.ZxSpence wrote: ↑Sat Mar 16, 2019 8:30 am It's worth pointing out that a two dimensional array is just a one dimensional array. You just index the row by X columns. However as connect 4 pieces only ever go down it's better to flip the model 90 degrees so that the columns are modelled but the rows are not. As the calculation should occur for 4 in a row once all gravity effects have been applied the checks for proximity are best modelled as you would with chess move checks, to whit recursive allowing an early exit.
No. His compile line ORGs the program at 0x8000, so the program starts there, then the DATA section goes directly above it, then the BSS section goes directly above that. Nothing will go in the lower contended bank. C doesn't know anything about switched bank memory on the 128K machines, and although there's library support for it you very much need to work to make it happen. It can't happen by chance.
Yes indeed.
The z80 is a special case though - it so happens that it is often faster to load and store from absolute memory addresses instead of loading and storing to local variables on the stack. Using the stack for local vars only saves time if the compiler is able to hold those values in registers as long as possible and then have the option not to write to memory if not needed. For reasoning about static variables, zsdcc does distinguish between volatile and non-volatile memory but I'm not sure how well that is obeyed.
The humans programming do though
As a 2d array the program must still perform a multiplication and addition to index to the chosen entry. If the numbers are nice, the multiplication may be turned into a bunch of shifts and adds so the penalty is not too bad. A manual 2d array where the programmer indexes into a 1d array with an actual multiply explicit in the code may not be optimized as often; I think the fact the compiler knows it's a 2d array means it will look for a nice shift-add solution for the entire index operation.ZxSpence wrote: ↑Sat Mar 16, 2019 8:30 am It's worth pointing out that a two dimensional array is just a one dimensional array. You just index the row by X columns. However as connect 4 pieces only ever go down it's better to flip the model 90 degrees so that the columns are modelled but the rows are not. As the calculation should occur for 4 in a row once all gravity effects have been applied the checks for proximity are best modelled as you would with chess move checks, to whit recursive allowing an early exit.