Add error checking and better reporting
This commit is contained in:
parent
ac48d84a81
commit
3d4cfc7a0c
47
main.c
47
main.c
@ -7,15 +7,13 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#define NRUNS 1000
|
#define NRUNS 10000
|
||||||
|
#define MAX_ADJ 8
|
||||||
enum { UNKNOWN = 0xfe, KILLER = 0xfd };
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FOUND_MINE,
|
FOUND_MINE,
|
||||||
@ -60,12 +58,14 @@ static void getadj(puzz_t field, int *x, int *y, uint8_t val)
|
|||||||
static update_res_t update(state_t *state, int *x_out, int *y_out)
|
static update_res_t update(state_t *state, int *x_out, int *y_out)
|
||||||
{
|
{
|
||||||
state->mines = state->unknown = 0;
|
state->mines = state->unknown = 0;
|
||||||
|
|
||||||
for (int y = 0; y < HEIGHT; ++y) {
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
for (int x = 0; x < WIDTH; ++x) {
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
state->mines += state->field[x][y] == MINE ? 1 : 0;
|
state->mines += state->field[x][y] == MINE ? 1 : 0;
|
||||||
state->unknown += state->field[x][y] == UNKNOWN ? 1 : 0;
|
state->unknown += state->field[x][y] == UNKNOWN ? 1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < HEIGHT; ++y) {
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
for (int x = 0; x < WIDTH; ++x) {
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
if (state->field[x][y] == UNKNOWN || state->field[x][y] == MINE)
|
if (state->field[x][y] == UNKNOWN || state->field[x][y] == MINE)
|
||||||
@ -93,7 +93,7 @@ static update_res_t update(state_t *state, int *x_out, int *y_out)
|
|||||||
return FOUND_NOTHING;
|
return FOUND_NOTHING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static status_t solve(void)
|
static status_t solve(int *turns_out)
|
||||||
{
|
{
|
||||||
state_t state = { .mines = 0, .unknown = WIDTH * HEIGHT };
|
state_t state = { .mines = 0, .unknown = WIDTH * HEIGHT };
|
||||||
memset(state.field, UNKNOWN, sizeof(puzz_t));
|
memset(state.field, UNKNOWN, sizeof(puzz_t));
|
||||||
@ -102,7 +102,9 @@ static status_t solve(void)
|
|||||||
int y = rand() % HEIGHT;
|
int y = rand() % HEIGHT;
|
||||||
|
|
||||||
status_t status;
|
status_t status;
|
||||||
|
int turns = 0;
|
||||||
do {
|
do {
|
||||||
|
++turns;
|
||||||
if (state.field[x][y] != MINE
|
if (state.field[x][y] != MINE
|
||||||
&& (status = probe(x, y, state.field)) == DEAD)
|
&& (status = probe(x, y, state.field)) == DEAD)
|
||||||
break;
|
break;
|
||||||
@ -112,12 +114,20 @@ static status_t solve(void)
|
|||||||
update_res = update(&state, &x, &y);
|
update_res = update(&state, &x, &y);
|
||||||
while (update_res == FOUND_MINE);
|
while (update_res == FOUND_MINE);
|
||||||
|
|
||||||
|
if (check(state.field) != OK) {
|
||||||
|
printf("Incorrect inference! State:\n");
|
||||||
|
print(state.field);
|
||||||
|
printsoln();
|
||||||
|
return INCORRECT;
|
||||||
|
}
|
||||||
|
|
||||||
if (update_res == FOUND_NOTHING) {
|
if (update_res == FOUND_NOTHING) {
|
||||||
x = rand() % WIDTH;
|
x = rand() % WIDTH;
|
||||||
y = rand() % HEIGHT;
|
y = rand() % HEIGHT;
|
||||||
}
|
}
|
||||||
} while (state.mines < NMINES);
|
} while (state.mines < NMINES || state.unknown > 0);
|
||||||
|
|
||||||
|
*turns_out = turns;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,14 +140,31 @@ int main(void)
|
|||||||
}
|
}
|
||||||
srand(tv.tv_usec);
|
srand(tv.tv_usec);
|
||||||
|
|
||||||
int nsolved = 0;
|
int nsolved = 0, nfirst = 0, nincorrect = 0, turns;
|
||||||
for (int i = 0; i < NRUNS; ++i) {
|
for (int i = 0; i < NRUNS; ++i) {
|
||||||
gen();
|
gen();
|
||||||
nsolved += solve() == OK ? 1 : 0;
|
switch (solve(&turns)) {
|
||||||
|
case DEAD:
|
||||||
|
if (turns == 1)
|
||||||
|
++nfirst;
|
||||||
|
break;
|
||||||
|
case OK:
|
||||||
|
++nsolved;
|
||||||
|
break;
|
||||||
|
case INCORRECT:
|
||||||
|
++nincorrect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double prop = (double)nsolved / NRUNS;
|
const double solved_prop = (double)nsolved / NRUNS;
|
||||||
printf("Solved %d/%d (%0.1f%%)\n", nsolved, NRUNS, 100 * prop);
|
const double incorrect_prop = (double)nincorrect / NRUNS;
|
||||||
|
const double first_prop = (double)nfirst / NRUNS;
|
||||||
|
const double solved_safe_start_prop = (double)nsolved / (NRUNS - nfirst);
|
||||||
|
printf("Solved %d (%0.1f%%)\n", nsolved, 100 * solved_prop);
|
||||||
|
printf("%d incorrect (%0.1f%%)\n", nincorrect, 100 * incorrect_prop);
|
||||||
|
printf("%d died on first turn (%0.1f%%)\n", nfirst, 100 * first_prop);
|
||||||
|
printf("%0.1f%% solved on safe start\n", 100 * solved_safe_start_prop);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
37
puzz.c
37
puzz.c
@ -38,17 +38,35 @@ void gen(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(void)
|
void print(puzz_t puzz)
|
||||||
{
|
{
|
||||||
puts("Solution:");
|
|
||||||
for (int y = 0; y < HEIGHT; ++y) {
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
for (int x = 0; x < WIDTH; ++x)
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
putchar(soln[x][y] == MINE ? 'x' : '0' + soln[x][y]);
|
char c;
|
||||||
|
switch (puzz[x][y]) {
|
||||||
|
case MINE:
|
||||||
|
c = 'x';
|
||||||
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
c = '.';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c = '0' + puzz[x][y];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
putchar(c);
|
||||||
|
}
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printsoln(void)
|
||||||
|
{
|
||||||
|
puts("Solution:");
|
||||||
|
print(soln);
|
||||||
|
}
|
||||||
|
|
||||||
static void scan_copy(int x, int y, puzz_t out)
|
static void scan_copy(int x, int y, puzz_t out)
|
||||||
{
|
{
|
||||||
assert(x >= 0 && x < WIDTH);
|
assert(x >= 0 && x < WIDTH);
|
||||||
@ -84,6 +102,17 @@ status_t probe(int x, int y, puzz_t out)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status_t check(puzz_t puzz)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
|
if (puzz[x][y] != UNKNOWN && puzz[x][y] != soln[x][y])
|
||||||
|
return INCORRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
int countadj(puzz_t field, int x, int y, uint8_t val)
|
int countadj(puzz_t field, int x, int y, uint8_t val)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
8
puzz.h
8
puzz.h
@ -12,14 +12,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
enum { MINE = 0xff };
|
enum { MINE = 0xff, UNKNOWN = 0xfe };
|
||||||
|
|
||||||
typedef uint8_t puzz_t[WIDTH][HEIGHT];
|
typedef uint8_t puzz_t[WIDTH][HEIGHT];
|
||||||
typedef enum { DEAD, OK } status_t;
|
typedef enum { DEAD, OK, INCORRECT } status_t;
|
||||||
|
|
||||||
void gen(void);
|
void gen(void);
|
||||||
void print(void);
|
void print(puzz_t puzz);
|
||||||
|
void printsoln(void);
|
||||||
status_t probe(int x, int y, puzz_t out);
|
status_t probe(int x, int y, puzz_t out);
|
||||||
|
status_t check(puzz_t puzz);
|
||||||
int countadj(puzz_t field, int x, int y, uint8_t val);
|
int countadj(puzz_t field, int x, int y, uint8_t val);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user