Compare commits

...

2 Commits

Author SHA1 Message Date
d2b71dff2e basic README 2021-01-27 13:56:34 +01:00
c6851b22c0 added timer for maxed calculation time. 2021-01-27 13:56:03 +01:00
7 changed files with 177 additions and 19 deletions

120
README.md Normal file
View File

@@ -0,0 +1,120 @@
# lceb - a Countdown solver in C.
`lceb` will find the best solutions(s) for the Numbers part of the
https://en.wikipedia.org/wiki/Countdown_(game_show)#Numbers_round
Coundown game ("_Le compte est Bon_" in French).
## Get the source
``` text
git clone https://git.bodiccea.tk/bruno/Le-Compte-est-Bon.git
```
## Build
To display mode debug information, you may first enable some DEBUG flags in the `Makefile`.
To build the binaries just run:
``` shellsession
$ make lceb
```
## Usage
### basic usage and output
```console
$ ./lceb 2 7 1 7 8 8
timer started...
mem: allocating operators working area (5 bytes)
new_stack: allocating 1024 stacks
get_node: allocating 1024 new nodes - total nodes=1024
target assigned (2).
SIGINT armed.
stacks : 30
ops combinations: 256
trees : 14
max evaluations : 537,600
Le compte est bon: 5 solutions with 2 ops (1st after 0.00030 secs).
1 8 7 - +
1 8 8 / +
1 7 7 / +
1 8 + 7 -
8 7 1 - -
Total time elapsed: 0.01327 secs, nodes/leaves evaluated:401,817/397,973
```
`lceb` takes at least 3 arguments: target number (the one to find), and from 2 to 6 use-able numbers.
The solutions are displayed by default in RPN notation (here `1 8 7 - +` means `(8-7)+1`).
### options
You can see the available options with `lceb -h`:
```console
$ ./lceb -h
usage: ./lceb [options] target n1 n2 [...n6]
Countdown game solver.
-1: Show only one solution (immediate stop when exact solution found
-c: Show solutions timer
-d TYPE: Solutions display type. TYPE can be:
r: RPN (default)
p: Polish
l: Lisp (binary operators)
i: Infix (full parentheses)
d: Suitable for dc(1) input
t: Tree
-i: Show intermediate solutions
-s: Do not show summary (time, nodes evaluated)
-t: Use less trees (WedderburnEtherington instead of Catalan)
-T TIMER: Will stop after TIMER 1/10th seconds
-h: This help
```
### the -t option
Using `-t` gives a large performance boost. Compare the two output below :
```console
$ ./lceb -di -c 997 4 8 25 2 5 3
timer started...
mem: allocating operators working area (6 bytes)
new_stack: allocating 1024 stacks
get_node: allocating 1024 new nodes - total nodes=1024
target assigned (997).
SIGINT armed.
stacks : 720
ops combinations: 1,024
trees : 42
max evaluations : 185,794,560
get_node: allocating 1024 new nodes - total nodes=2048
get_node: allocating 1024 new nodes - total nodes=3072
get_node: allocating 1024 new nodes - total nodes=4096
get_node: allocating 1024 new nodes - total nodes=5120
Le compte est bon: 3 solutions with 3 ops (1st after 0.00841 secs).
0.00841 secs: (((5 * 8) * 25) - 3)
0.00841 secs: (((5 * 25) * 8) - 3)
0.00841 secs: (((8 * 25) * 5) - 3)
Total time elapsed: 3.40713 secs, nodes/leaves evaluated:138,979,332/132,474,697
```
```console
$ ./lceb -di -t -c 997 4 8 25 2 5 3
timer started...
mem: allocating operators working area (6 bytes)
new_stack: allocating 1024 stacks
get_node: allocating 1024 new nodes - total nodes=1024
target assigned (997).
SIGINT armed.
stacks : 720
ops combinations: 1,024
trees : 6
max evaluations : 26,542,080
get_node: allocating 1024 new nodes - total nodes=2048
Le compte est bon: 3 solutions with 3 ops (1st after 0.15392 secs).
0.15392 secs: (((5 * 8) * 25) - 3)
0.15392 secs: (((5 * 25) * 8) - 3)
0.15393 secs: (((8 * 25) * 5) - 3)
Total time elapsed: 0.47460 secs, nodes/leaves evaluated:18,793,369/18,650,735
```
It should be noted that having general performance improvement does not mean that the first solution will be found sooner.

14
best.c
View File

@@ -12,7 +12,7 @@ static int bestops=MAXINT;
static BEST bests[1024*10]; /* TODO: should be dynamic */
static int nbests=0;
extern int displaytimer;
extern int displaytimer, firstonly;
int displayintermediate=0;
int displaytype=0;
@@ -120,12 +120,16 @@ void print_best(node, values, pops, depth)
void print_bests()
{
int i=0;
printf("BEST SOLUTION: res=%d diff=%d ops=%d ", bests[i].res, bestdiff, bestops);
int i=0, j=firstonly? 1: nbests;
if (bestdiff==0) {
printf("Le compte est bon: %d solutions with %d ops ", nbests, bestops);
} else {
printf("Found %d results with difference %d and %d ops ", nbests, bestdiff, bestops);
}
//if (displaytimer)
printf("found after %.5f secs.", get_timer(bests[i].timer));
printf("(1st after %.5f secs).", get_timer(bests[i].timer));
putchar('\n');
for (i=0; i<nbests; ++i) {
for (i=0; i<j; ++i) {
//print_best(bests[i].root, bests[i].values, bests[i].oper, 0);
//printf("%5d =", bests[i].res);
if (displaytimer)

4
eval.c
View File

@@ -82,7 +82,7 @@ int eval_node(node, depth, pvals, pops, ncalcs)
if (val1<val2) {
NODE *tmp=node->left;
# ifdef DEBUG_EVAL2
printf("eval: Sub: swapping val1=%d val2=%d\n", val1, val2);
printf("eval: Div: swapping val1=%d val2=%d\n", val1, val2);
# endif
node->left=node->right;
node->right=tmp;
@@ -115,7 +115,7 @@ int eval_node(node, depth, pvals, pops, ncalcs)
}
}
if (sigint_received) {
print_bests();
print_results();
exit(1);
}

11
lceb.c
View File

@@ -20,6 +20,7 @@
static char *cmd;
static int treetype=TREE_CATALAN;
static int maxmillisecs=0;
extern int displaytimer, displayintermediate, displaytype, firstonly;
int displaysummary=1;
@@ -42,7 +43,7 @@ void help()
{
usage();
fprintf(stderr, "Countdown game solver.\n");
fprintf(stderr, " -1: Stops immediately when one solution is found\n");
fprintf(stderr, " -1: Show only one solution (immediate stop when exact solution found\n");
fprintf(stderr, " -c: Show solutions timer\n");
fprintf(stderr, " -d TYPE: Solutions display type. TYPE can be:\n");
fprintf(stderr, " r: RPN (default)\n");
@@ -54,6 +55,7 @@ void help()
fprintf(stderr, " -i: Show intermediate solutions\n");
fprintf(stderr, " -s: Do not show summary (time, nodes evaluated)\n");
fprintf(stderr, " -t: Use less trees (WedderburnEtherington instead of Catalan)\n");
fprintf(stderr, " -T TIMER: Will stop after TIMER 1/10th seconds\n");
fprintf(stderr, " -h: This help\n");
}
@@ -75,7 +77,7 @@ int main(ac, av)
TREE *tree;
char intarray[1024];
char *comb;
char *options="1thcisd:";
char *options="1thcisd:T:";
int option;
# ifdef DEBUG_MAINLOOP
int eval;
@@ -124,6 +126,9 @@ int main(ac, av)
case 's':
displaysummary=0;
break;
case 'T':
maxmillisecs=atoi(optarg)*100;
break;
case 't':
treetype=TREE_WEDDERBURN;
break;
@@ -143,6 +148,8 @@ int main(ac, av)
}
setlocale(LC_ALL, ""); /* to use "%'d" in printf */
start_timer();
if (maxmillisecs)
set_alarm(maxmillisecs);
target=atoi(av[optind]);
stacksize=ac-optind-1;
nops=stacksize-1;

4
lceb.h
View File

@@ -7,6 +7,9 @@
#define MAX(a,b) (((a)>(b))?(a):(b))
#define ABS(a) (((a)<0)?-(a):(a))
#define NANOSEC 1000000000.0
#define MILLISEC 1000
typedef enum {
Nop='N',
Add='+',
@@ -71,6 +74,7 @@ extern void print_results();
/* signal.c */
extern void set_intr();
extern int stopped();
extern void set_alarm(int ms);
/* tree.c */
extern NODE *get_node();

View File

@@ -1,11 +1,15 @@
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include "lceb.h"
int sigint_received=0;
static void stopall()
static void stopall(signum)
int signum;
{
printf("SIGINT RECEIVED: aborting eval\n");
printf("SIGNAL %d RECEIVED: aborting eval\n", signum);
sigint_received=1;
}
@@ -20,14 +24,35 @@ void set_intr()
sig.sa_handler = stopall;
sigemptyset(&sig.sa_mask);
//sigaddset(&new.sa_mask, SIGINT);
//sigaddset(&new.sa_mask, SIGALRM);
sig.sa_flags = 0;
//sigaction(SIGINT, NULL, &old);
//if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &sig, NULL);
sigaction(SIGALRM, &sig, NULL);
# ifdef DEBUG
printf("SIGINT and SIGALRM armed.\n");
printf("SIGINT armed.\n");
# endif
}
void set_alarm(ms)
int ms;
{
struct sigaction sa;
struct itimerval timer;
/* Install timer_handler as the signal handler for SIGVTALRM. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = stopall;
sigaction (SIGUSR1, &sa, NULL);
timer.it_value.tv_sec = ms/1000;
timer.it_value.tv_usec = (ms%1000)*1000;
# ifdef DEBUG
printf("alarm clock set to %.2f secs.\n",
timer.it_value.tv_sec + timer.it_value.tv_usec / 1000000.);
# endif
/* ... and every 250 msec after that. */
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 0;
/* Start a virtual timer. It counts down whenever this process is executing. */
setitimer (ITIMER_REAL, &timer, NULL);
}

View File

@@ -1,9 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NANOSEC 1000000000.0
#define MILLISEC 1000
#include "lceb.h"
int displaytimer=0;
static struct timespec start;