Gameboy Development Forum

Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)

You are not logged in.

#1 2023-08-05 23:57:19

Raine
New member
Registered: 2023-08-05
Posts: 4

I'm making checkers for the gameboy and need your help.

So far Ive got everything but kings and win conditions working. Right now I'm focusing on kinging. I somehow managed to code everything as one singular .c file so far. Here it is in it's entirety, sprites and all.

#include <gb/gb.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <gbdk/font.h>

#define BLACK_PLAYER 0
#define WHITE_PLAYER 1
#define MAX_BLACK_PIECES 12
#define MAX_WHITE_PIECES 12
#define SQUARE_SIZE 16
#define SPRITE_TILE_BLACK 4
#define SPRITE_TILE_WHITE 16
#define DEBOUNCE_DELAY 6 // Adjust this value based on your requirements (in milliseconds)
UBYTE lastButtonState = 0; // Variable to store the previous button state
UINT16 debounceTimer = 0; // Variable to track the time since the last button press
int selectedPieceIndex = -1;

UINT8 cursorx = 28;
UINT8 cursory = 28;
UBYTE joypad_input;
UINT8 currentPlayer = BLACK_PLAYER;
int selectedCoords = 0;
bool pieceSelected = false;

unsigned char tile1[] = {
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
unsigned char tile2[] = {
  0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,
  0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00
};
unsigned char tile3[] = {
  0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,
  0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF
};
unsigned char map[] = {
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
unsigned char squareTL[] = {
  0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,
  0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80
};
unsigned char squareTR[] = {
  0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
};
unsigned char squareBL[] = {
  0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
  0x80,0x80,0x80,0x80,0x80,0x80,0xFF,0xFF
};
unsigned char squareBR[] = {
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF
};
unsigned char black_piece[] = {
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
unsigned char white_piece[] = {
  0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,
  0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00
};
unsigned char currentPlayerBlackText[] = {
    0x00, 0x00, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x00, 0x42, 0x6C, 0x61, 0x63, 0x6B, 0x00, 0x00
};
unsigned char currentPlayerWhiteText[] = {
    0x00, 0x00, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x00, 0x57, 0x68, 0x69, 0x74, 0x65, 0x00, 0x00
};
unsigned char blackKing[] = {
  0xFF,0xFF,0xDB,0xFF,0x66,0xFF,0x81,0xFF,
  0x81,0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xFF
};
unsigned char whiteKing[] = {
  0xFF,0x00,0xDB,0x24,0x66,0x99,0x81,0x7E,
  0x81,0x7E,0xC3,0x3C,0xFF,0x00,0xFF,0x00
};
void moveSquare() {
      move_sprite(0, cursorx - 4, cursory - 4);
      move_sprite(1, cursorx + 4, cursory - 4);
      move_sprite(2, cursorx - 4, cursory + 4);
      move_sprite(3, cursorx + 4, cursory + 4);
}

// Define a structure to store piece information (x and y coordinates)
typedef struct {
    UINT8 x;
    UINT8 y;
    bool isKing; // Field to store whether the piece is a king or not
} Piece;
Piece blackPieces[MAX_BLACK_PIECES] = {
    {44, 28}, {76, 28}, {108, 28}, {140, 28},
    {28, 44}, {60, 44}, {92, 44}, {124, 44},
    {44, 60}, {76, 60}, {108, 60}, {140, 60}
};
Piece whitePieces[MAX_WHITE_PIECES] = {
    {28, 140}, {60, 140}, {92, 140}, {124, 140},
    {44, 124}, {76, 124}, {108, 124}, {140, 124},
    {28, 108}, {60, 108}, {92, 108}, {124, 108}
};
void dpad() {
    if (joypad_input & J_RIGHT) {
        cursorx = cursorx + SQUARE_SIZE;
    }
    if (joypad_input & J_LEFT) {
        cursorx = cursorx - SQUARE_SIZE;
    }
    if (joypad_input & J_UP) {
        cursory = cursory - SQUARE_SIZE;
    }
    if (joypad_input & J_DOWN) {
        cursory = cursory + SQUARE_SIZE;
    }
    moveSquare();
}
void font() {
    font_t min_font;
    font_init();
    min_font = font_load(font_ibm_fixed);
    font_set(min_font);
}
void printTurn() {
    if (currentPlayer == BLACK_PLAYER){
        set_win_tiles(2, 0, 16, 1, currentPlayerBlackText);
    } else {
        set_win_tiles(2, 0, 16, 1, currentPlayerWhiteText);
    }
    move_win(7, 136);
}
void printbkg() {
    set_bkg_data(1, 1, tile1);
    set_bkg_data(2, 1, tile2);
    set_bkg_data(3, 1, tile3);
    set_bkg_tiles(0, 0, 20, 18, map);
}
void printSquare() {
    set_sprite_data(0, 1, squareTL);
    set_sprite_data(1, 1, squareTR);
    set_sprite_data(2, 1, squareBL);
    set_sprite_data(3, 1, squareBR);
    set_sprite_tile(0, 0);
    set_sprite_tile(1, 1);
    set_sprite_tile(2, 2);
    set_sprite_tile(3, 3);
    moveSquare();
}
void printBlack() {
    set_sprite_data(4, 12, black_piece);
    set_sprite_data(8, 12, blackKing);

    for (int i = 0; i < 12; i++){
        if (blackPieces[i].isKing) {
            set_sprite_tile(i + 4, 8); // Use the black king sprite tile
        } else {
            set_sprite_tile(i + 4, 4); // Use the black regular piece sprite tile
        }
        move_sprite(i + 4, blackPieces[i].x, blackPieces[i].y);
    }
}
void printWhite() {
    set_sprite_data(5, 12, white_piece);
    set_sprite_data(20, 12, whiteKing);

    for (int i = 0; i < 12; i++){
        if (whitePieces[i].isKing) {
            set_sprite_tile(i + 16, 20); // Use the white king sprite tile
        } else {
            set_sprite_tile(i + 16, 5); // Use the white regular piece sprite tile
        }
        move_sprite(i + 16, whitePieces[i].x, whitePieces[i].y);
    }
}
void modifyCoordinates(Piece input[], int index, int newX, int newY) {
    // Check if the given index is within the valid range of the array
    if (index >= 0) {
        // Modify the coordinates at the given index
        input[index].x = newX;
        input[index].y = newY;
    }
}
bool isMoveWithinBoard(UINT8 x, UINT8 y) {
    return (x >= 20 && x <=140 && y >= 20 && y <= 140);
}
bool checkCapture(UINT8 capturedX, UINT8 capturedY, Piece* opponentPieces) {

    // Check collision for each piece
    for (int i = 0; i < 12; i++) {
        UINT8 pieceX = opponentPieces[i].x;
        UINT8 pieceY = opponentPieces[i].y;

        // Calculate the boundaries of the sprite
        UINT8 pieceLeft = pieceX - 4;
        UINT8 pieceRight = pieceX + 4;
        UINT8 pieceTop = pieceY - 4;
        UINT8 pieceBottom = pieceY + 4;
        UINT8 tileLeft = capturedX - 4;
        UINT8 tileRight = capturedX + 4;
        UINT8 tileTop = capturedY - 4;
        UINT8 tileBottom = capturedY + 4;

        // Check for collision by comparing boundaries
        if (tileLeft <= pieceRight && tileRight >= pieceLeft &&
            tileTop <= pieceBottom && tileBottom >= pieceTop) {
            opponentPieces[i].x = 0;
            opponentPieces[i].y = 0;
            return true;
        }
    }
    return false;
}
// Function to check if a move is valid
bool isValidMove(UINT8 cursorx, UINT8 cursory, UINT8 currentPlayer, int selectedCoords) {
    Piece* pieces;
    Piece* opponentPieces;
    int numPieces;
    int numOpponentPieces;

    // Set the correct piece array based on the current player
    if (currentPlayer == BLACK_PLAYER) {
        pieces = blackPieces;
        opponentPieces = whitePieces;
        numPieces = MAX_BLACK_PIECES;
        numOpponentPieces = MAX_WHITE_PIECES;
    } else {
        pieces = whitePieces;
        opponentPieces = blackPieces;
        numPieces = MAX_WHITE_PIECES;
        numOpponentPieces = MAX_BLACK_PIECES;
    }

    // Check if the cursor is within the bounds of the board
    if (cursorx > 160 || cursorx <= 20 || cursory > 160 || cursory <= 20) {
        return false;
    }

    // Check if the selectedCoords is within the valid range of the array
    if (selectedCoords < 0 || selectedCoords >= numPieces) {
        return false;
    }

    // Calculate the distance moved in x and y direction
    int dx = cursorx - pieces[selectedCoords].x;
    int dy = cursory - pieces[selectedCoords].y;

    // Check if the piece is moving diagonally
    if (abs(dx) != abs(dy)) {
        return false;
    }

    // Check if the piece is moving forward or backward based on the player's color
    if ((currentPlayer == BLACK_PLAYER && dy < 0 && !pieces[selectedCoords].isKing) ||
        (currentPlayer == WHITE_PLAYER && dy > 0 && !pieces[selectedCoords].isKing)) {
        return false;
    }

    // Check if the target position is empty
    for (int i = 0; i < numPieces; i++) {
        if (whitePieces[i].x == cursorx && whitePieces[i].y == cursory) {
            return false;
        }
        if (blackPieces[i].x == cursorx && blackPieces[i].y == cursory) {
            return false;
        }
    }
    // Check if the move is promoting the piece to a king
    if ((currentPlayer == BLACK_PLAYER && cursory <= 32) ||
        (currentPlayer == WHITE_PLAYER && cursory >= 126)) {
        pieces[selectedCoords].isKing = true;
        // Add the following lines to update the sprite tile when the piece is promoted
        if (currentPlayer == BLACK_PLAYER) {
            set_sprite_tile(selectedPieceIndex, 8); // Use the black king sprite tile
        } else {
            set_sprite_tile(selectedPieceIndex, 20); // Use the white king sprite tile
        }
    }

    // Check if the move is a "capture" move
    if (abs(dx) > 2 * SQUARE_SIZE || abs(dy) > 2 * SQUARE_SIZE) {
        return false;
    } else if (abs(dx) == 2 * SQUARE_SIZE || abs(dy) == 2 * SQUARE_SIZE) {
        // Check if the move is a "capture" move
        UINT8 capturedX = pieces[selectedCoords].x + (dx / 2);
        UINT8 capturedY = pieces[selectedCoords].y + (dy / 2);
        if (checkCapture(capturedX, capturedY, opponentPieces)) {
            return true;
        } else {
            return false;
        }
    }


    return true;
}
// Function to check collision between cursor and pieces
bool checkCollision(UINT8 cursorx, UINT8 cursory, int currentPlayer) {
    int numPieces;
    Piece* pieces;

    // Set the correct piece array based on the current player
    if (currentPlayer == BLACK_PLAYER) {
        pieces = blackPieces;
        numPieces = 12;
    } else {
        pieces = whitePieces;
        numPieces = 12;
    }

    // Check collision for each piece
    for (int i = 0; i < numPieces; i++) {
        UINT8 pieceX = pieces[i].x;
        UINT8 pieceY = pieces[i].y;

        // Calculate the boundaries of the piece sprite
        UINT8 pieceLeft = pieceX - 4;
        UINT8 pieceRight = pieceX + 4;
        UINT8 pieceTop = pieceY - 4;
        UINT8 pieceBottom = pieceY + 4;

        // Calculate the boundaries of the cursor sprite
        UINT8 cursorLeft = cursorx - 4;
        UINT8 cursorRight = cursorx + 4;
        UINT8 cursorTop = cursory - 4;
        UINT8 cursorBottom = cursory + 4;

        // Check for collision by comparing boundaries
        if (cursorLeft <= pieceRight && cursorRight >= pieceLeft &&
            cursorTop <= pieceBottom && cursorBottom >= pieceTop) {
            if (currentPlayer == BLACK_PLAYER) {
                selectedCoords = i;
                selectedPieceIndex = i + 4;
            } else {
                selectedCoords = i;
                selectedPieceIndex = i + 16;
            }
            return true;
        }
    }
    // No collision with any piece
    selectedPieceIndex = -1;
    return false;
}

void main() {
    font();
    printTurn();
    printbkg();
    printSquare();
    printBlack();
    printWhite();
    SHOW_BKG; 
    SHOW_SPRITES;
    SHOW_WIN;
    Piece* input;
    while(1) {
        joypad_input = joypad();
            // Debounce the button input
        if (joypad_input != lastButtonState) {
            debounceTimer = 0; // Reset the debounce timer
            lastButtonState = joypad_input;
        } else if (debounceTimer < DEBOUNCE_DELAY) {
            debounceTimer += 100; // Increment the debounce timer based on the loop delay (100ms in this code)
            continue; // Skip processing input until the debounce delay is reached
        }
       
        dpad();
        if (joypad_input & J_A) {
            if (pieceSelected == false) {
                checkCollision(cursorx, cursory, currentPlayer);
                if (currentPlayer == BLACK_PLAYER) {
                    if (selectedPieceIndex >= 4 && selectedPieceIndex < 16){
                        pieceSelected = true;
                    }
                } else if (selectedPieceIndex >= 16 && selectedPieceIndex < 28) {
                        pieceSelected = true;
                }
            }
        }
        while (pieceSelected == true) {
            delay(100);
            joypad_input = joypad(); // Update the input inside the loop
            dpad();
            move_sprite(selectedPieceIndex, cursorx, cursory);
            if (joypad_input & J_A) {
                if (currentPlayer == BLACK_PLAYER){
                    input = blackPieces;
                } else {
                    input = whitePieces;
                }
                if (cursorx == input[selectedCoords].x && cursory == input[selectedCoords].y){
                    //do nothing
                } else {
                    if (isValidMove(cursorx, cursory, currentPlayer, selectedCoords)) {
                        modifyCoordinates(input, selectedCoords, cursorx, cursory);
                        pieceSelected = false;
                        if (currentPlayer == BLACK_PLAYER) {
                            currentPlayer = WHITE_PLAYER;
                        } else {
                            currentPlayer = BLACK_PLAYER;
                        }
                        printBlack();
                        printWhite();
                    }
                }
                printTurn();
                break; // Exit the loop after a piece has been moved
            }
            if (joypad_input & J_B) {
                pieceSelected = false;
                printBlack();
                printWhite();
                break;
            }
        }
        delay(100);
    }
}

Offline

 

#2 2023-08-06 16:13:22

Raine
New member
Registered: 2023-08-05
Posts: 4

Re: I'm making checkers for the gameboy and need your help.

The current issue is that kinging isn't working. I realize i didn't make that clear in my original post. I've looked at the code for hours, and I can't figure out the issue. I'm just looking for help, not someone to do it for me.

Offline

 

#3 2023-08-06 19:04:08

Raine
New member
Registered: 2023-08-05
Posts: 4

Re: I'm making checkers for the gameboy and need your help.

update: i fixed it.

Offline

 

#4 2023-08-06 19:05:46

Raine
New member
Registered: 2023-08-05
Posts: 4

Re: I'm making checkers for the gameboy and need your help.

now i just need to work on win conditions. If anybody knows of a way to quickly check if there are any legal moves left, please tell me, because this is gonna be a pain and a half to code.

Offline

 

Board footer

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson