Anyone interested in a Pokitto port of Minesweeper?

I’ve just finished publishing the v2.0.0 version of my Minesweeper game for Arduboy.

Would anyone be interested in a Pokitto port of this?
An initial version would be a 1:1 port, no colour or anything.
(Though I could add colour later if people were interested enough.)

This game has been created with the programming talents of @Pharap, the artistic talents of @Vampirics, and the bilingual talents of @Vampirics, @eried and @Zockeromi, and… whatever the hell @filmote’s got (:P) who all happen to be ‘dual citizens’ of both the Arduboy and Pokitto forums.

6 Likes

@Pharap : I was looking for a game to make a tutorial on how to make Pokitto color graphics.

Plus, if this is done with Arduboy2 lib, I could refresh it to a working status

I’m in.

4 Likes

Count me in to add colours to everything when you get there.

@Pharap I would love to follow what you do in the ‘porting process’. I need to port my own things to the Pokitto as well so that might give me some usefull informations.

1 Like

I would be interested as well.

1 Like

I’d be working with VSCode + PlatformIO because that’s what I developed the v2 of Minesweeper with, so perhaps it would be worth putting the Pokitto port of Arduboy2 into a separate repo as well?

I haven’t checked, but I’m pretty sure Minesweeper will run on an older version of Arduboy2. I’ll check what the earliest version it will support is tomorrow.

Either before or at the same time as @jonne is working on Arduboy2,
I’ll make one that substitutes Arduboy2 code with PokittoLib code so you can get an idea of what’s equivalent to what.
(Something I’ve done precisely once before, with PhysixArduboy being converted to PhysixPokitto.)


Who knows, maybe we’ll even be able to add to Minesweeper’s language repertoire?

I took a quick look.

The main problem is the extensive use of EEPROM features in the game. Those need to be worked around.

Pharap… excuse my dumbness, just wondering how it has been possible to call member functions directly like this?

C:\PokittoLib_Git\PokittoLib_curr\Examples\Minesweeper\Minesweeper\CreditsState.cpp|111|error: cannot call member function ‘void Sprites::drawOverwrite(int16_t, int16_t, const uint8_t*, uint8_t)’ without object|

The source of the error:

	// Draw the logo
	{
		constexpr const uint16_t x = CalculateCentreX(Images::PharapLogoWidth);
		Sprites::drawOverwrite(x, y, Images::PharapLogo, 0);
		y += Images::PharapLogoHeight + border;
	}

EDIT: I’m not really working on this. It’s just been my experience that I could get most AB games working within 2 hours so I gave it a quick-and-dirty shot

EDIT2: I’m not really working on this - yet. I will set up the whole thing in PlatformIO and then we’ll take a crack at it

drawOverwrite is a static member function in the original Arduboy2 library. (See static members.)

Basically a static function is like having a free function in the class’s scope - it doesn’t have access to the this pointer because it doesn’t operate on an object, it behaves just like a free function.

(And for the really technically inclined, it doesn’t use the thiscallcalling convention either - “This calling convention is used for calling C++ non-static member functions.”)

There’s a quirk of C++ whereby you can call static member functions using the same syntax for non-static member functions (i.e. object.function()) but strictly speaking you don’t need an object to access a static member function, you can just use the scope operator on the class type, which is what I’m doing in this example (i.e. Class::function()).

When you say “original” does it mean I have an outdated AB2 lib in use? In that, the member functions of Sprites class are not static.

/**
 * @file Sprites.h
 * \brief
 * A class for drawing animated sprites from image and mask bitmaps.
 */

#ifndef Sprites_h
#define Sprites_h

#include "Arduboy2.h"

#define SPRITE_MASKED 1
#define SPRITE_UNMASKED 2
#define SPRITE_OVERWRITE 2
#define SPRITE_PLUS_MASK 3
#define SPRITE_IS_MASK 250
#define SPRITE_IS_MASK_ERASE 251
#define SPRITE_AUTO_MODE 255

/** \brief
 * A class for drawing animated sprites from image and mask bitmaps.
 *
 * \details
 * The functions in this class will draw to the screen buffer an image
 * contained in an array located in program memory. A mask can also be
 * specified or implied, which dictates how existing pixels in the buffer,
 * within the image boundaries, will be affected.
 *
 * A sprite or mask array contains one or more "frames". Each frame is intended
 * to show whatever the sprite represents in a different position, such as the
 * various poses for a running or jumping character. By specifying a different
 * frame each time the sprite is drawn, it can be animated.
 *
 * Each array begins with values for the width and height of the sprite, in
 * pixels. The width can be any value. The height must be a multiple of
 * 8 pixels, but with proper masking, a sprite of any height can be created.
 *
 * After the width and height values, the remainder of the array contains the
 * image and/or mask data for each frame. Each byte represents a vertical
 * column of 8 pixels with the least significant bit (bit 0) at the top.
 * The bytes are drawn as 8 pixel high rows from left to right, top to bottom.
 * When the end of a row is reached, as specified by the width value, the next
 * byte in the array will be the start of the next row.
 *
 * Data for each frame after the first one immediately follows the previous
 * frame. Frame numbers start at 0.
 */
class Sprites
{
  public:
    /** \brief
     * Draw a sprite using a separate image and mask array.
     *
     * \param x,y The coordinates of the top left pixel location.
     * \param bitmap A pointer to the array containing the image frames.
     * \param mask A pointer to the array containing the mask frames.
     * \param frame The frame number of the image to draw.
     * \param mask_frame The frame number for the mask to use (can be different
     * from the image frame number).
     *
     * \details
     * An array containing the image frames, and another array containing
     * corresponding mask frames, are used to draw a sprite.
     *
     * Bits set to 1 in the mask indicate that the pixel will be set to the
     * value of the corresponding image bit. Bits set to 0 in the mask will be
     * left unchanged.
     *
     *     image  mask   before  after
     *
     *     .....  .OOO.  .....   .....
     *     ..O..  OOOOO  .....   ..O..
     *     OO.OO  OO.OO  .....   OO.OO
     *     ..O..  OOOOO  .....   ..O..
     *     .....  .OOO.  .....   .....
     *
     *     image  mask   before  after
     *
     *     .....  .OOO.  OOOOO   O...O
     *     ..O..  OOOOO  OOOOO   ..O..
     *     OO.OO  OOOOO  OOOOO   OO.OO
     *     ..O..  OOOOO  OOOOO   ..O..
     *     .....  .OOO.  OOOOO   O...O
     */
    void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
                          const uint8_t *mask, uint8_t frame, uint8_t mask_frame);

    /** \brief
     * Draw a sprite using an array containing both image and mask values.
     *
     * \param x,y The coordinates of the top left pixel location.
     * \param bitmap A pointer to the array containing the image/mask frames.
     * \param frame The frame number of the image to draw.
     *
     * \details
     * An array containing combined image and mask data is used to draw a
     * sprite. Bytes are given in pairs with the first byte representing the
     * image pixels and the second byte specifying the corresponding mask.
     * The width given in the array still specifies the image width, so each
     * row of image and mask bytes will be twice the width value.
     *
     * Bits set to 1 in the mask indicate that the pixel will be set to the
     * value of the corresponding image bit. Bits set to 0 in the mask will be
     * left unchanged.
     *
     *     image  mask   before  after
     *
     *     .....  .OOO.  .....   .....
     *     ..O..  OOOOO  .....   ..O..
     *     OO.OO  OO.OO  .....   OO.OO
     *     ..O..  OOOOO  .....   ..O..
     *     .....  .OOO.  .....   .....
     *
     *     image  mask   before  after
     *
     *     .....  .OOO.  OOOOO   O...O
     *     ..O..  OOOOO  OOOOO   ..O..
     *     OO.OO  OOOOO  OOOOO   OO.OO
     *     ..O..  OOOOO  OOOOO   ..O..
     *     .....  .OOO.  OOOOO   O...O
     */
    void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);

    /** \brief
     * Draw a sprite by replacing the existing content completely.
     *
     * \param x,y The coordinates of the top left pixel location.
     * \param bitmap A pointer to the array containing the image frames.
     * \param frame The frame number of the image to draw.
     *
     * \details
     * A sprite is drawn by overwriting the pixels in the buffer with the data
     * from the specified frame in the array. No masking is done. A bit set
     * to 1 in the frame will set the pixel to 1 in the buffer, and a 0 in the
     * array will set a 0 in the buffer.
     *
     *     image  before  after
     *
     *     .....  .....   .....
     *     ..O..  .....   ..O..
     *     OO.OO  .....   OO.OO
     *     ..O..  .....   ..O..
     *     .....  .....   .....
     *
     *     image  before  after
     *
     *     .....  OOOOO   .....
     *     ..O..  OOOOO   ..O..
     *     OO.OO  OOOOO   OO.OO
     *     ..O..  OOOOO   ..O..
     *     .....  OOOOO   .....
     */
    void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);

    /** \brief
     * "Erase" a sprite.
     *
     * \param x,y The coordinates of the top left pixel location.
     * \param bitmap A pointer to the array containing the image frames.
     * \param frame The frame number of the image to erase.
     *
     * \details
     * The data from the specified frame in the array is used to erase a
     * sprite. To "erase" a sprite, bits set to 1 in the frame will set the
     * corresponding pixel in the buffer to 0. Frame bits set to 0 will remain
     * unchanged in the buffer.
     *
     *     image  before  after
     *
     *     .....  .....   .....
     *     ..O..  .....   .....
     *     OO.OO  .....   .....
     *     ..O..  .....   .....
     *     .....  .....   .....
     *
     *     image  before  after
     *
     *     .....  OOOOO   OOOOO
     *     ..O..  OOOOO   OO.OO
     *     OO.OO  OOOOO   ..O..
     *     ..O..  OOOOO   OO.OO
     *     .....  OOOOO   OOOOO
     */
    void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);

    /** \brief
     * Draw a sprite using only the bits set to 1.
     *
     * \param x,y The coordinates of the top left pixel location.
     * \param bitmap A pointer to the array containing the image frames.
     * \param frame The frame number of the image to draw.
     *
     * \details
     * Bits set to 1 in the frame will be used to draw the sprite by setting
     * the corresponding pixel in the buffer to 1. Bits set to 0 in the frame
     * will remain unchanged in the buffer.
     *
     *     image  before  after
     *
     *     .....  .....   .....
     *     ..O..  .....   ..O..
     *     OO.OO  .....   OO.OO
     *     ..O..  .....   ..O..
     *     .....  .....   .....
     *
     *     image  before  after
     *
     *     .....  OOOOO   OOOOO  (no change because all pixels were
     *     ..O..  OOOOO   OOOOO  already white)
     *     OO.OO  OOOOO   OOOOO
     *     ..O..  OOOOO   OOOOO
     *     .....  OOOOO   OOOOO
     */
    void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);

    // Master function. Needs to be abstracted into separate function for
    // every render type.
    // (Not officially part of the API)
    void draw(int16_t x, int16_t y,
              const uint8_t *bitmap, uint8_t frame,
              const uint8_t *mask, uint8_t sprite_frame,
              uint8_t drawMode);

    // (Not officially part of the API)
    void drawBitmap(int16_t x, int16_t y,
                    const uint8_t *bitmap, const uint8_t *mask,
                    int8_t w, int8_t h, uint8_t draw_mode);
};

#endif

I was under the impression that they had always been static,
but I double checked and in fact they weren’t always static.
They’ve been static since 12 April 2017 (specifically this commit).

So yes, the Sprites definition in use is outdated.
By my estimates the current implementation must be based on Arduboy2 v3.1.1 or earlier.

I’ve started chipping away at it slowly, but there’s a lot of code and there’s some complicated stuff.

For example, I’m using avr/eeprom.h which currently doesn’t have a Pokitto equivalent, so I’ll have to write one and check out the licencing.

I’m sort of hovering between getting the Arduboy2 port working and doing a manual translation.
I’ll probably do both just to see what the experience is like.

I could probably provide a Dutch translation if you would want to add an additional language…

Wrong. There is a 1:1 eeprom wrapper BUT Pokitto::Cookie should be used because it will not mess up any other saves

EDIT: i mean i went through the trouble of porting that particular part of arduino. Its in pokittoeeprom.cpp

Whereabouts?

I found eeprom_read_byte and eeprom_write_byte in iap.cpp, but the argument type is wrong and that’s only a small part of avr/eeprom.h.

I’ll worry about that when I’ve got it compiling.

PokittoEPROM in POKITTO_HW

My 2 cents: comment eeprom calls out 1st

PokittoEEPROM.h only implements the Arduino EEPROM wrapper,
I’m using avr/eeprom.h which is something different:

https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

I’ve written some wrapper code for most of what I need:

#pragma once

#include <Pokitto.h>
#include <cstdint>

/*template< typename T >
T eeprom_read_T(const std::uint16_t * index)
{
	T data;
	readEEPROM(index, &data, sizeof(T));
	return data;
}

template< typename T >
void eeprom_write_T(const std::uint16_t * index, const T & data)
{
	writeEEPROM(index, &data, sizeof(T));
}*/

std::uint8_t eeprom_read_byte(const std::uint8_t * index)
{
    std::uint8_t value;
    readEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint8_t));
	return value;
}

std::uint16_t eeprom_read_word(const std::uint16_t * index)
{
    std::uint16_t value;
    readEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint16_t));
	return value;
}

std::uint32_t eeprom_read_dword(const std::uint32_t * index)
{
    std::uint32_t value;
    readEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint32_t));
	return value;
}

float eeprom_read_float(const float * index)
{
    float value;
    readEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(float));
	return value;
}

void eeprom_read_block(void * destination, const void * source, size_t count)
{
    readEEPROM((std::uint16_t *)source, (std::uint8_t *)destination, count);
}



void eeprom_write_byte(std::uint8_t * index, std::uint8_t value)
{
    writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint8_t));
}

void eeprom_write_word(std::uint16_t * index, std::uint16_t value)
{
    writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint16_t));
}

void eeprom_write_dword(std::uint32_t * index, std::uint32_t value)
{
    writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint32_t));
}

void eeprom_write_float(float * index, float value)
{
    writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(float));
}

void eeprom_write_block(void * destination, const void * source, size_t count)
{
    writeEEPROM((std::uint16_t *)source, (std::uint8_t *)destination, count);
}



void eeprom_update_byte(std::uint8_t * index, std::uint8_t value)
{
	auto current = eeprom_read_byte((std::uint16_t *)index);
	if(current != value)
		writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint8_t));
}

void eeprom_update_word(std::uint16_t * index, std::uint16_t value)
{
	auto current = eeprom_read_word((std::uint16_t *)index);
	if(current != value)
		writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint16_t));
}

void eeprom_update_dword(std::uint32_t * index, std::uint32_t value)
{
	auto current = eeprom_read_dword((std::uint16_t *)index);
	if(current != value)
		writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(std::uint32_t));
}

void eeprom_update_float(float * index, float value)
{
	auto current = eeprom_read_float((std::uint16_t *)index);
	if(current != value)
    writeEEPROM((std::uint16_t *)index, (std::uint8_t *)&value, sizeof(float));
}

void eeprom_update_block(void * destination, const void * source, size_t count)
{
    writeEEPROM((std::uint16_t *)source, (std::uint8_t *)destination, count);
}

Ah. My bad. Please continue.

1 Like

Dutch would be a good addition.
I’ll probably wait until after it’s all been ported before arranging that.

If you want to get a heads start then take a look at this:

I’ve given up on trying to do a direct substitution.
I got through all the source errors but some left over linker errors, which I can’t be bothered to fix.

I think the best bet is to get the Arduboy2 port working on a simpler game before trying to get Minesweeper working.