0

I made a backtracking code which solves a sudoku and I want to display it on the screen using C libraries. I know that I can use GTK, but I think it is too complicated for my level.

How can I create an image with the solution displayed on a sudoku grid. It doesn't need to create the grid too, it is ok if I use an image with a grid as an input. So basically, I have a matrix (the sudoku solution) and I want to display it as an image.

I mention that I use VSC on a Virtual Machine with Mint.

  • Is is reasonably easy to output the solution as HTML code, and easier as CSV for a spreadsheet. Then take a screenshot. – Weather Vane May 26 '21 at 16:00
  • You might also consider outputting SVG. – Lee Daniel Crocker May 26 '21 at 19:05
  • I assume you already have a 2D matrix that represents your board position. You might consider using `SDL2` instead of `GTK` as it's much simpler and can display directly to a window. Or, you can create an image file and then use `system` to invoke a display program (e.g. ImageMagick's `display` command). It's pretty easy to output a `.bmp` file. Or, you could create a `.ppm` format file--that's even simpler. You could even output text graphics as a sudoku representation isn't too complex. ImageMagick's `convert` can convert a format to any other format – Craig Estey May 26 '21 at 19:07
  • Thank you for your help! I still have a question. @CraigEstey, How do I convert my 2D matrix who looks like a sudoku into a matrix that can be displayed as a ppm or bmp. I assume that I need to have a matrix of pixels for that. – Ionut Becheru May 26 '21 at 21:51

1 Answers1

0

From my top comments ...

I assume you already have a 2D matrix that represents your board position.

You might consider using SDL2 instead of GTK as it's much simpler and can display directly to a window.

Or, you can create an image file and then use system to invoke a display program (e.g. ImageMagick's display command). It's pretty easy to output a .bmp file. Or, you could create a .ppm format file--that's even simpler.

You could even output text graphics as a sudoku representation isn't too complex.

ImageMagick's convert can convert a format to any other format.


Thank you for your help! I still have a question. @CraigEstey, How do I convert my 2D matrix who looks like a sudoku into a matrix that can be displayed as a ppm or bmp. I assume that I need to have a matrix of pixels for that. – Ionut Becheru

Yes, you'll need a pixel matrix.

But, if you just create that yourself, you'll have to create a bunch of drawing primitives for drawing rectangles, drawing text fonts, adding colors, etc. In particular, drawing text into an image is complicated (we need a font definition).

So, once again, I suggest using a graphics package that does all that for you. And, again, I suggest SDL2.

In addition to the development package for SDL2, [on fedora, at least], you'll need to install the development package for SDL2_ttf. And, you'll need the font packages.

So, you'll need:

SDL2-devel
SDL2_ttf-devel
gnu-free-sans-fonts
gnu-free-fonts-common

The following code is adapted and enhanced from: How to render text in SDL2?

I added the grid drawing, box drawing, adapted the text draw to draw the sudoku numbers inside their boxes, and saving of the image to a .ppm file.

To build, do (e.g.):

cc -o sudrend sudrend.c `pkg-config --libs --cflags SDL2_ttf` -g

Here is the code. It's a bit messy [and crude] but it works:

#include <stdlib.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#if 0
#define WINDOW_WIDTH        512
#endif
#if 0
#define WINDOW_WIDTH        (64 * 9)
#endif
#if 1
#define WINDOW_WIDTH        (32 * 9)
#endif
#define WINDOW_HEIGHT       (WINDOW_WIDTH)
#define GRIDMAX             (WINDOW_WIDTH / 3)

#define SUDBOXMAX           3
#define SUDXMAX             (SUDBOXMAX * 3)
#define SUDYMAX             (SUDBOXMAX * 3)
typedef int board_t[SUDYMAX][SUDXMAX];

typedef unsigned char byte;

typedef struct {
    byte r;
    byte g;
    byte b;
#ifdef PIX32
    byte a;
#endif
} pixel_t;

board_t brd;

TTF_Font *font;

/*
- x, y: upper left corner.
- texture, rect: outputs.
*/
void
get_text_and_rect(SDL_Renderer *renderer,
    int x, int y,
    char *text, TTF_Font *font,
    SDL_Texture **texture, SDL_Rect *rect)
{
    int text_width;
    int text_height;
    SDL_Surface *surface;
#if 0
    SDL_Color textColor = { 255, 255, 255, 0 };
#endif
#if 0
    SDL_Color textColor = { 237, 135, 45, 0 };
#endif
#if 1
    SDL_Color textColor = { 255, 69, 0, 0 };
#endif

    surface = TTF_RenderText_Solid(font,text,textColor);
    *texture = SDL_CreateTextureFromSurface(renderer,surface);

    text_width = surface->w;
    text_height = surface->h;

    SDL_FreeSurface(surface);

    rect->x = x;
    rect->y = y;
    rect->w = text_width;
    rect->h = text_height;
}

// genboard -- generate sudoku board
void
genboard(board_t brd)
{

    for (int y = 0;  y < SUDYMAX;  ++y) {
        for (int x = 0;  x < SUDYMAX;  ++x)
            brd[y][x] = 0;
    }

    int maxpt = (rand() % (SUDYMAX * SUDXMAX)) + 1;

    for (int curpt = 0;  curpt < maxpt;  ++curpt) {
        int y = rand() % SUDYMAX;
        int x = rand() % SUDYMAX;
        int val = rand() % 10;
        brd[y][x] = val;
    }
}

// prtboard -- print sudoku board as text
void
prtboard(board_t brd)
{

    for (int y = 0;  y < SUDYMAX;  ++y) {
        if ((y % 3) == 0)
            printf("\n");
        for (int x = 0;  x < SUDYMAX;  ++x) {
            if ((x % 3) == 0)
                printf(" ");
            int val = brd[y][x];
            if (val == 0)
                printf(" *");
            else
                printf(" %d",val);
        }
        printf("\n");
    }
}

void
drawgrid(SDL_Renderer *renderer)
{
    int gridmax;
    int boxmax;
    SDL_Rect rect;

#if 0
    SDL_SetRenderDrawColor(renderer, 0, 0xFF, 0, 0);
#endif
#if 0
    SDL_SetRenderDrawColor(renderer,143,188,143,0);
#endif
#if 1
    SDL_SetRenderDrawColor(renderer,0,0,139,0);
#endif

    gridmax = GRIDMAX;
    boxmax = SUDYMAX;
    for (int ybox = 0;  ybox < boxmax;  ++ybox) {
        int ybase = ybox * gridmax;
        rect.y = ybase;
        rect.h = gridmax;
        for (int xbox = 0;  xbox < boxmax;  ++xbox) {
            int xbase = xbox * gridmax;
            rect.x = xbase;
            rect.w = gridmax;
            SDL_RenderDrawRect(renderer,&rect);
        }
    }
}

void
drawbox(SDL_Renderer *renderer)
{
    int gridmax;
    int boxmax;
    SDL_Rect rect;

#if 0
    SDL_SetRenderDrawColor(renderer, 255, 216, 0, 0);
#else
    SDL_SetRenderDrawColor(renderer,255,250,205,0);
#endif
    gridmax = GRIDMAX / 3;
    boxmax = SUDYMAX * 3;
    for (int ybox = 0;  ybox < boxmax;  ++ybox) {
        int ybase = ybox * gridmax;
        rect.y = ybase;
        rect.h = gridmax;
        for (int xbox = 0;  xbox < boxmax;  ++xbox) {
            int xbase = xbox * gridmax;
            rect.x = xbase;
            rect.w = gridmax;
            SDL_RenderDrawRect(renderer,&rect);
        }
    }
}

void
drawtext(SDL_Renderer *renderer)
{
    int gridmax;
    int boxmax_y;
    int boxmax_x;
    SDL_Rect grect;
    SDL_Rect trect;
    SDL_Texture *texture;
    char buf[2];

#if 0
    SDL_SetRenderDrawColor(renderer, 255, 216, 0, 0);
#endif
#if 0
    SDL_SetRenderDrawColor(renderer,255,250,205,0);
#endif
#if 1
    SDL_SetRenderDrawColor(renderer,0,0,0,0);
#endif

    gridmax = GRIDMAX / 3;
#if 0
    boxmax_y = SUDYMAX * 3;
    boxmax_x = SUDXMAX * 3;
#else
    boxmax_y = SUDYMAX;
    boxmax_x = SUDXMAX;
#endif
    for (int ybox = 0;  ybox < boxmax_y;  ++ybox) {
        int ybase = ybox * gridmax;
        grect.y = ybase + gridmax / 3;
        grect.h = gridmax / 3;

        for (int xbox = 0;  xbox < boxmax_x;  ++xbox) {
            int chr = brd[ybox][xbox];
            if (chr != 0)
                buf[0] = chr + '0';
            else
                buf[0] = ' ';
            buf[1] = 0;
            get_text_and_rect(renderer, 0, 0, buf, font, &texture, &trect);

            int xbase = xbox * gridmax;
            grect.x = xbase + gridmax / 3;
            grect.w = gridmax / 3;

            SDL_RenderCopy(renderer, texture, &trect, &grect);

            SDL_DestroyTexture(texture);
        }
    }
}

// imgsave -- save image to P6 .ppm file
void
imgsave(SDL_Renderer *renderer,int imgno)
{
    int pitch;
    FILE *xf;
    char file[1000];
    pixel_t *pixmap;

    pitch = sizeof(pixel_t) * WINDOW_WIDTH;
    pixmap = malloc(pitch * WINDOW_HEIGHT);

    // get pixel map from renderer image
#ifdef PIX32
    SDL_RenderReadPixels(renderer,NULL,SDL_PIXELFORMAT_RGBA32,pixmap,pitch);
#else
    SDL_RenderReadPixels(renderer,NULL,SDL_PIXELFORMAT_RGB24,pixmap,pitch);
#endif

    sprintf(file,"img%3.3d.ppm",imgno);
    xf = fopen(file,"w");

    fprintf(xf,"P6\n");
    fprintf(xf,"%d %d",WINDOW_WIDTH,WINDOW_HEIGHT);
    fprintf(xf," %d\n",255);

#ifdef PIX32
    pixel_t *pixcur = &pixmap[0];
    pixel_t *pixlim = &pixmap[WINDOW_WIDTH * WINDOW_HEIGHT];
    for (;  pixcur < pixlim;  ++pixcur) {
        fputc(pixcur->r,xf);
        fputc(pixcur->g,xf);
        fputc(pixcur->b,xf);
    }
#else
    fwrite(pixmap,sizeof(pixel_t),WINDOW_WIDTH * WINDOW_HEIGHT,xf);
#endif

    fclose(xf);

    free(pixmap);
}

void
fontinit(const char *font_tail)
{
    const char *dir;
    char font_path[1000];

    TTF_Init();

    for (int idx = 0;  idx <= 1;  ++idx) {
        switch (idx) {
        case 0:
            dir = NULL;
            break;
        default:  // NOTE: my system needed this
            dir = "/usr/share/fonts/gnu-free";
            break;
        }

        if (dir == NULL)
            strcpy(font_path,font_tail);
        else
            sprintf(font_path,"%s/%s",dir,font_tail);

        font = TTF_OpenFont(font_path, 24);
        if (font != NULL)
            break;
    }

    if (font == NULL) {
        fprintf(stderr, "error: font not found\n");
        exit(EXIT_FAILURE);
    }
}

int
main(int argc, char **argv)
{
    SDL_Event event;
    SDL_Renderer *renderer;
    SDL_Window *window;
    char *font_path;
    int quit;

    switch (argc) {
    case 1:
        font_path = "FreeSans.ttf";
        break;
    case 2:
        font_path = argv[1];
        break;
    default:
        fprintf(stderr, "error: too many arguments\n");
        exit(EXIT_FAILURE);
        break;
    }

    genboard(brd);
    prtboard(brd);

    /* Inint TTF. */
    SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
    SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window,
        &renderer);

    fontinit(font_path);

#if 0
    SDL_Rect rect1, rect2;
    SDL_Texture *texture1, *texture2;
    get_text_and_rect(renderer, 0, 0, "hello", font, &texture1, &rect1);
    get_text_and_rect(renderer, 0, rect1.y + rect1.h, "world", font,
        &texture2, &rect2);
#endif

    quit = 0;
    while (1) {
        while (SDL_PollEvent(&event) == 1) {
            if (event.type == SDL_QUIT)
                quit = 1;
        }
        if (quit)
            break;

        // set background
#if 0
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
#else
        SDL_SetRenderDrawColor(renderer,135,206,250,0);
#endif
        SDL_RenderClear(renderer);

        drawbox(renderer);
        drawgrid(renderer);
        drawtext(renderer);

        /* Use TTF textures. */
#if 0
        SDL_RenderCopy(renderer, texture1, NULL, &rect1);
        SDL_RenderCopy(renderer, texture2, NULL, &rect2);
#endif

        imgsave(renderer,0);

        SDL_RenderPresent(renderer);
    }

    /* Deinit TTF. */
#if 0
    SDL_DestroyTexture(texture1);
    SDL_DestroyTexture(texture2);
#endif
    TTF_Quit();

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return EXIT_SUCCESS;
}

Here is the text output of the program (Note that the program just generates a random test matrix that may or may not be a valid sudoku):

  2 4 7  9 * 7  * 6 *
  * 6 *  * * *  9 * 1
  7 1 *  * * 3  * * 6

  * * 6  * 7 *  5 7 *
  * * 9  * 5 *  * * *
  * * 7  3 * *  * 8 *

  7 * 1  * * *  2 * 8
  5 * 6  * 2 6  * * *
  * 2 *  * * *  * * 3

Here is the image file that was generated. The program saved it to img000.ppm. I then did: convert img000.ppm img000.png [using ImageMagick's convert]

enter image description here

Craig Estey
  • 30,627
  • 4
  • 24
  • 48