|
#include "rlib.h"
|
|
#include "sudoku.h"
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include "footer.h"
|
|
|
|
unsigned long long global_m_lock_count = 0;
|
|
pthread_mutex_t * global_m_lock = NULL;
|
|
|
|
#define WITH_MUTEX(src,update_footer) \
|
|
if(global_m_lock == NULL){ \
|
|
global_m_lock = malloc(sizeof(pthread_mutex_t)); \
|
|
pthread_mutex_init(global_m_lock, NULL); \
|
|
} \
|
|
pthread_mutex_lock(global_m_lock); \
|
|
global_m_lock_count++; \
|
|
if(update_footer) { \
|
|
footer_printf("l:%s:%d (%lld)",__func__,__LINE__,global_m_lock_count); \
|
|
} \
|
|
src \
|
|
if(update_footer) { \
|
|
footer_printf("u:%s:%d (%lld)",__func__,__LINE__,global_m_lock_count); \
|
|
} \
|
|
pthread_mutex_unlock(global_m_lock);
|
|
|
|
|
|
typedef struct thread_data_t {
|
|
int puzzle[N][N];
|
|
int solution[N][N];
|
|
pthread_t thread;
|
|
unsigned long long steps;
|
|
unsigned long long steps_total;
|
|
uint complexity;
|
|
uint result_complexity;
|
|
uint solved_count;
|
|
uint initial_count_minimum;
|
|
uint initial_count_maximum;
|
|
uint result_initial_count;
|
|
uint id;
|
|
pthread_mutex_t lock;
|
|
bool is_done;
|
|
nsecs_t start;
|
|
nsecs_t finish;
|
|
nsecs_t duration;
|
|
} thread_data_t;
|
|
|
|
thread_data_t * thread_data_create(){
|
|
thread_data_t * data = malloc(sizeof(thread_data_t));
|
|
memset(data,0,sizeof(thread_data_t));
|
|
return data;
|
|
}
|
|
void thread_data_free(thread_data_t * data){
|
|
|
|
free(data);
|
|
}
|
|
|
|
|
|
|
|
int * grid_with_minimal_complexity(thread_data_t * tdata){
|
|
int * grid = grid_new();
|
|
int * grid_game = NULL;
|
|
while(true){
|
|
tdata->result_initial_count = rand_int(tdata->initial_count_minimum,tdata->initial_count_maximum);
|
|
|
|
grid_set_random_free_cells(grid,tdata->result_initial_count);
|
|
//print_grid(grid,false);
|
|
grid_game = grid_copy(grid);
|
|
//footer_printf("Solving: %ld", tdata->result_initial_count);
|
|
|
|
tdata->start = nsecs();
|
|
//tdata->steps = 0;
|
|
tdata->result_complexity = rsolve(grid,&tdata->steps);
|
|
tdata->steps_total += tdata->steps;
|
|
tdata->steps = 0;
|
|
tdata->solved_count++;
|
|
if(tdata->result_complexity == 0){
|
|
//print_grid(grid,true);
|
|
//exit(5);
|
|
//footer_printf("thread %d failed validation",tdata->id);
|
|
}else{
|
|
|
|
//footer_printf("thread %d solved: %d",tdata->id, tdata->result_complexity);
|
|
}
|
|
//if(tdata->solution)
|
|
// free(tdata->solution);
|
|
|
|
|
|
|
|
WITH_MUTEX({
|
|
memcpy(tdata->solution,grid,N*N*sizeof(int));
|
|
memcpy(tdata->puzzle,grid_game,N*N*sizeof(int));
|
|
|
|
},false);
|
|
if(tdata->result_complexity >= tdata->complexity){
|
|
break;
|
|
}else{
|
|
free(grid_game);
|
|
grid_reset(grid);
|
|
}
|
|
}
|
|
return grid_game;
|
|
}
|
|
|
|
void * generate_game(void * arg){
|
|
thread_data_t * tdata = (thread_data_t *)arg;
|
|
tick();
|
|
//unsigned int * result_complexity = (int *)calloc(sizeof(int),1);
|
|
//unsigned int * result_initial_count = (int *)calloc(sizeof(int),1);
|
|
//rr_disable_stdout();
|
|
|
|
int * puzzle = grid_with_minimal_complexity(tdata);
|
|
|
|
//if(tdata->puzzle){
|
|
//free(tdata->puzzle);
|
|
|
|
//}
|
|
WITH_MUTEX({
|
|
tdata->finish = nsecs();
|
|
tdata->duration = tdata->finish - tdata->start;
|
|
tdata->is_done = true;
|
|
},false);
|
|
return NULL;
|
|
}
|
|
|
|
void thread_data_to_json_object(rjson_t * json ,thread_data_t * data){
|
|
rjson_object_start(json);
|
|
rjson_kv_int(json,"id",data->id);
|
|
rjson_kv_int(json,"solved_count",data->solved_count);
|
|
rjson_kv_number(json,"steps_total",data->steps_total + data->steps);
|
|
rjson_kv_number(json,"steps",data->steps);
|
|
rjson_kv_number(json,"result_initial_count",data->result_initial_count);
|
|
rjson_kv_duration(json,"start",data->start);
|
|
rjson_kv_duration(json,"finish",data->finish);
|
|
rjson_kv_duration(json,"duration",nsecs() - data->start);
|
|
rjson_kv_string(json,"puzzle",grid_to_string(data->puzzle));
|
|
rjson_kv_string(json,"solution",grid_to_string(data->solution));
|
|
rjson_object_close(json);
|
|
}
|
|
char * thread_data_to_json(thread_data_t * data, int runner_count){
|
|
rjson_t * json = rjson();
|
|
rjson_array_start(json);
|
|
for(int i = 0; i < runner_count; i++){
|
|
thread_data_to_json_object(json,&data[i]);
|
|
}
|
|
rjson_array_close(json);
|
|
|
|
char * content = strdup(json->content);
|
|
rjson_free(json);
|
|
return content;
|
|
|
|
}
|
|
|
|
char * thread_data_to_string(thread_data_t * data){
|
|
static char result[4096];
|
|
memset(result,0,sizeof(result));
|
|
sprintf(result,"id:%d\tcomplexity: %u\t total solved: %d \tsteps total: %s\tsteps current: %s\tinitc: %d\ttime: %s",
|
|
data->id,
|
|
data->result_complexity,
|
|
data->solved_count,
|
|
rtempc(rformat_number(data->steps_total + data->steps)),
|
|
rtempc(rformat_number(data->steps)),
|
|
data->result_initial_count,
|
|
format_time(nsecs() - data->start)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
char * runner_status_to_string(thread_data_t * runners, unsigned int runner_count){
|
|
static char result[1024*1024];
|
|
memset(result,0,sizeof(result));
|
|
|
|
for(uint i = 0; i < runner_count; i++){
|
|
strcat(result,thread_data_to_string(&runners[i]));
|
|
strcat(result,"\n");
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
typedef struct serve_arguments_t {
|
|
thread_data_t * runners;
|
|
unsigned int runner_count;
|
|
int port;
|
|
nsecs_t time_winner;
|
|
int puzzle_winner[N][N];
|
|
int solution_winner[N][N];
|
|
char start_timestamp[30];
|
|
} serve_arguments_t;
|
|
|
|
|
|
serve_arguments_t serve_arguments;
|
|
|
|
void get_totals(thread_data_t * runners, unsigned int runner_count, ulong * steps_total, ulong * solved_total,nsecs_t * longest_running){
|
|
*steps_total = 0;
|
|
*solved_total = 0;
|
|
*longest_running = 0;
|
|
nsecs_t end_time = nsecs();
|
|
for(unsigned int i = 0; i < runner_count; i++){
|
|
*steps_total += runners[i].steps_total + runners[i].steps;
|
|
*solved_total += runners[i].solved_count;
|
|
nsecs_t duration = runners[i].start ? end_time - runners[i].start : 0;
|
|
if(duration > *longest_running){
|
|
*longest_running = duration;
|
|
}
|
|
}
|
|
}
|
|
|
|
void http_response(rhttp_request_t * r, char * content){
|
|
char headers[strlen(content) + 1000];
|
|
sprintf(headers,"HTTP/1.1 200 OK\r\n"
|
|
"Content-Length:%ld\r\n"
|
|
"Content-Type: text/html\r\n"
|
|
"Connection: close\r\n\r\n%s",
|
|
strlen(content),content);
|
|
rhttp_send_drain(r->c,headers,0);
|
|
close(r->c);
|
|
}
|
|
|
|
|
|
|
|
int request_handler_json_processes(rhttp_request_t*r){
|
|
if(!strncmp(r->path,"/json/processes",strlen("/json/processes"))){
|
|
WITH_MUTEX({
|
|
char * content = thread_data_to_json(serve_arguments.runners,serve_arguments.runner_count); // runner_status_to_string(serve_arguments.runners,serve_arguments.runner_count);
|
|
http_response(r,content);
|
|
|
|
free(content);
|
|
},false);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char * statistics_to_json_object(serve_arguments_t * args, rjson_t * json ,thread_data_t * data){
|
|
ulong steps_total = 0;
|
|
ulong solved_total = 0;
|
|
nsecs_t longest_running = 0;
|
|
get_totals(args->runners, args->runner_count, &steps_total, &solved_total, &longest_running);
|
|
|
|
rjson_object_start(json);
|
|
rjson_kv_string(json,"start",args->start_timestamp);
|
|
rjson_kv_number(json,"steps_total",steps_total);
|
|
rjson_kv_number(json,"solved_total",solved_total);
|
|
rjson_kv_number(json,"steps_per_puzzle",solved_total != 0 ? steps_total / (solved_total + args->runner_count) : 0);
|
|
rjson_kv_string(json,"longest_running",format_time(longest_running));
|
|
rjson_kv_string(json,"time_winner",format_time(args->time_winner));
|
|
rjson_kv_string(json,"puzzle_winner",grid_to_string(args->puzzle_winner));
|
|
rjson_kv_string(json,"solution_winner",grid_to_string(args->solution_winner));
|
|
rjson_object_close(json);
|
|
return json;
|
|
}
|
|
|
|
int request_handler_json_statistics(rhttp_request_t *r ){
|
|
serve_arguments_t args = *(serve_arguments_t *)r->context;
|
|
if(!strncmp(r->path, "/json/statistics",strlen("/json/statistics"))){
|
|
rjson_t * json = rjson();
|
|
statistics_to_json_object(&args,json,args.runners);
|
|
char * content = strdup(json->content);
|
|
rjson_free(json);
|
|
http_response(r,content);
|
|
free(content);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
int request_handler_root(rhttp_request_t *r){
|
|
serve_arguments_t args = *(serve_arguments_t *)r->context;
|
|
if(!strcmp(r->path,"/")){
|
|
WITH_MUTEX({
|
|
char * content = runner_status_to_string(args.runners,args.runner_count);
|
|
ulong steps_total = 0;
|
|
ulong solved_total = 0;
|
|
nsecs_t longest_running = 0;
|
|
get_totals(args.runners, args.runner_count, &steps_total, &solved_total, &longest_running);
|
|
char html[1024*1024*2] = {0};
|
|
sprintf(html, "<html><meta http-equiv=\"refresh\" content=\"5\"><head>"
|
|
"<body style=\"background-color:black;color:white;\"><pre>"
|
|
"%s"
|
|
"Started: %s\n"
|
|
"\n"
|
|
"Steps total: %s\n"
|
|
"Solved total: %s\n"
|
|
"Steps per puzzle: %s\n"
|
|
"Longest running: %s\n"
|
|
"Generation time hardest puzzle: %s\n"
|
|
"\n"
|
|
"Hardest puzzle:\n"
|
|
"%s\n"
|
|
"Solution:\n"
|
|
"%s\n"
|
|
"</pre>"
|
|
"</body></html>",
|
|
content,
|
|
args.start_timestamp,
|
|
rtempc(rformat_number(steps_total)),
|
|
rtempc(rformat_number(solved_total)),
|
|
rtempc(rformat_number(solved_total != 0 ? steps_total / (solved_total + args.runner_count) : 0)),
|
|
rtempc(format_time(longest_running)),
|
|
rtempc(format_time(args.time_winner)),
|
|
rtempc(grid_to_string(args.puzzle_winner)),
|
|
rtempc(grid_to_string(args.solution_winner))
|
|
);
|
|
char response[1024*1024*3];
|
|
memset(response,0,sizeof(response));
|
|
sprintf(response,"HTTP/1.1 200 OK\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Content-Type: text/html\r\n"
|
|
"Connection: close:\r\n\r\n",
|
|
strlen(html));
|
|
rhttp_send_drain(r->c, response,0);
|
|
rhttp_send_drain(r->c, html,0);
|
|
},true);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int request_handler_empty(rhttp_request_t * r){
|
|
if(!strncmp(r->path,"/empty",strlen("/empty"))){
|
|
int grid[N][N];
|
|
memset(grid,0,N*N*sizeof(int));
|
|
char * content = grid_to_string(grid);
|
|
char response[1024];
|
|
response[0] = 0;
|
|
sprintf(
|
|
response,
|
|
"HTTP/1.1 200 OK\r\nContent-Length:%zu\r\nConnection: close\r\n\r\n",
|
|
strlen(content)
|
|
);
|
|
rhttp_send_drain(r->c,response,0);
|
|
rhttp_send_drain(r->c,content,0);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int request_handler_404(rhttp_request_t *r){
|
|
char content[] = "HTTP/1.1 404 Document not found\r\nContent-Length:3\r\nConnection:close\r\n\r\n404";
|
|
rhttp_send_drain(r->c,content,0);
|
|
return 1;
|
|
}
|
|
|
|
int request_handler(rhttp_request_t * r){
|
|
rhttp_request_handler_t request_handlers[] ={
|
|
request_handler_root,
|
|
request_handler_empty,
|
|
request_handler_json_processes,
|
|
request_handler_json_statistics,
|
|
rhttp_file_request_handler,
|
|
request_handler_404,
|
|
NULL
|
|
};
|
|
int i = -1;
|
|
while(request_handlers[++i])
|
|
if(request_handlers[i](r))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void * serve_thread(void *arg){
|
|
serve_arguments_t * arguments = (serve_arguments_t *)arg;
|
|
|
|
rhttp_serve("0.0.0.0",arguments->port,1024,1,1,request_handler,(void *)arguments);
|
|
return NULL;
|
|
}
|
|
|
|
void generate_games(unsigned int game_count, unsigned int timeout, unsigned int complexity){
|
|
pthread_t thread_serve;
|
|
|
|
|
|
thread_data_t runners[game_count];
|
|
serve_arguments.runners = runners;
|
|
serve_arguments.runner_count = game_count;
|
|
serve_arguments.port = 9999;
|
|
serve_arguments.time_winner = 0;
|
|
strcpy(serve_arguments.start_timestamp,rstrtimestamp());
|
|
memset(serve_arguments.solution_winner,0,sizeof(serve_arguments.solution_winner));
|
|
memset(serve_arguments.puzzle_winner,0,sizeof(serve_arguments.puzzle_winner));
|
|
|
|
pthread_create(&thread_serve,0,serve_thread,(void *)&serve_arguments);
|
|
//pthread_mutex_t lock;
|
|
//pthread_mutex_init(&lock,NULL);
|
|
for(unsigned int i = 0; i < game_count; i++){
|
|
runners[i].initial_count_maximum = 30;
|
|
runners[i].initial_count_minimum = 1;
|
|
runners[i].complexity = complexity;
|
|
runners[i].is_done = false;
|
|
runners[i].id = i;
|
|
memset(runners[i].solution,0,N*N*sizeof(int));
|
|
memset(runners[i].puzzle,0,N*N*sizeof(int));
|
|
runners[i].solved_count = 0;
|
|
runners[i].duration = 0;
|
|
runners[i].steps = 0;
|
|
runners[i].steps_total = 0;
|
|
//runners[i].lock = lock;
|
|
pthread_create(&runners[i].thread,NULL,generate_game,(void *)(&runners[i]));
|
|
}
|
|
unsigned int highest_complexity = complexity;
|
|
for(unsigned int i = 0; i < timeout; i++){
|
|
sleep(1);
|
|
WITH_MUTEX({
|
|
footer_printf("main");
|
|
//pthread_mutex_lock(&lock);
|
|
for(unsigned int ithread = 0; ithread < game_count; ithread++){
|
|
if(runners[ithread].is_done){
|
|
pthread_join(runners[ithread].thread,NULL);
|
|
|
|
if(runners[ithread].result_complexity > highest_complexity){
|
|
highest_complexity = runners[ithread].result_complexity;
|
|
for(uint j = 0; j < game_count; j++){
|
|
runners[j].complexity = highest_complexity;
|
|
|
|
}
|
|
|
|
printf("\r\n");
|
|
print_grid(runners[ithread].puzzle,true);
|
|
printf("\n");
|
|
print_grid(runners[ithread].solution,false);
|
|
|
|
|
|
memcpy(serve_arguments.puzzle_winner,runners[ithread].puzzle,N*N*sizeof(int));
|
|
memcpy(serve_arguments.solution_winner,runners[ithread].solution,N*N*sizeof(int));
|
|
serve_arguments.time_winner = runners[ithread].duration;
|
|
|
|
|
|
printf("Thread %d is done (%s)\n",ithread,format_time(runners[ithread].duration));
|
|
printf("Complexity: %ld\n",runners[ithread].result_complexity);
|
|
printf("Initial values: %ld\n",runners[ithread].result_initial_count);
|
|
|
|
}
|
|
runners[ithread].is_done = false;
|
|
//free(runners[ithread].puzzle);
|
|
//runners[ithread].puzzle = NULL;
|
|
//free(runners[ithread].solution);
|
|
//runners[ithread].solution = NULL;
|
|
pthread_create(&runners[ithread].thread,NULL,generate_game,(void *)(&runners[ithread]));
|
|
|
|
}
|
|
}
|
|
|
|
//pthread_mutex_unlock(&lock);
|
|
},true);
|
|
}
|
|
|
|
for(unsigned int i = 0; i < game_count; i++){
|
|
|
|
pthread_cancel(runners[i].thread);
|
|
|
|
}
|
|
pthread_testcancel();
|
|
}
|
|
|
|
int main() {
|
|
setbuf(stdout,NULL);
|
|
|
|
srand(time(NULL));
|
|
// setbuf(stdout,0);
|
|
int cores = sysconf(_SC_NPROCESSORS_ONLN);
|
|
int threads = cores * 4 - 1;
|
|
sprintf(footer_prefix, "Cores: %d - Threads: %d - ", cores,threads);
|
|
// Highest: 1481563980
|
|
generate_games(threads ,13371337,1);
|
|
exit(0);
|
|
|
|
/*
|
|
0 0 9 0 0 0 0 0 0
|
|
0 0 0 0 0 0 3 0 0
|
|
6 0 1 5 0 2 0 0 0
|
|
0 0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 8 0 0 0
|
|
9 0 4 7 0 0 0 0 5
|
|
0 0 7 8 0 0 0 0 0
|
|
0 0 8 0 0 0 0 0 0
|
|
0 4 0 0 3 0 0 6 2
|
|
|
|
Attempts:
|
|
213212476
|
|
|
|
|
|
*/
|
|
/*
|
|
0 0 0 0 0 0 0 0 0
|
|
0 0 0 5 0 0 0 0 0
|
|
0 0 0 7 0 2 0 0 0
|
|
0 0 0 0 0 1 0 0 0
|
|
0 0 4 0 0 0 0 9 3
|
|
0 0 0 0 0 8 0 0 0
|
|
0 0 0 4 0 5 0 0 0
|
|
0 0 0 0 0 0 0 0 2
|
|
0 0 8 3 0 0 0 0 5
|
|
Thread 2 is done (459.04s)
|
|
Complexity: 125004041
|
|
Initial values: 14
|
|
*/
|
|
/*
|
|
0 0 0 0 0 0 0 0 0
|
|
0 8 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 6 5 0
|
|
0 0 4 0 0 0 9 0 0
|
|
0 0 0 2 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0 0
|
|
0 0 0 0 4 0 0 9 1
|
|
0 0 0 0 6 5 0 0 0
|
|
Thread 2 is done (1528.38s)
|
|
Complexity: 748250144
|
|
Initial values: 11
|
|
*/
|
|
/*0 3 0 0 0 0 0 0 0
|
|
0 0 5 0 0 0 0 0 0
|
|
0 0 0 0 0 0 1 0 0
|
|
0 0 0 0 0 0 0 4 0
|
|
0 0 0 0 9 0 0 0 0
|
|
0 0 9 0 3 0 0 0 0
|
|
0 0 0 0 0 6 0 0 0
|
|
0 0 4 0 0 0 0 7 0
|
|
0 6 7 0 0 4 0 2 0
|
|
Thread 0 is done (2635.64s)
|
|
Complexity: 1481563980
|
|
Initial values: 14*/
|
|
/*
|
|
GROOTSTE!
|
|
Complexity: 774858414
|
|
0 0 0 4 0 0 9 0 0
|
|
0 0 0 0 0 0 0 0 0
|
|
0 2 0 5 0 8 0 0 0
|
|
5 0 2 0 0 0 0 0 3
|
|
0 0 0 0 0 0 0 0 0
|
|
0 7 0 0 0 0 0 0 0
|
|
0 9 0 0 0 0 4 3 0
|
|
2 0 0 0 5 7 0 6 0
|
|
0 0 7 1 0 0 2 0 0
|
|
Generated in: 1393.95s
|
|
*/
|
|
/*
|
|
ONEINDIG?
|
|
0 2 0 0 0 0 0 0 0
|
|
0 0 4 0 0 0 0 0 0
|
|
6 0 1 8 0 0 0 0 0
|
|
3 0 6 0 0 0 8 0 0
|
|
0 0 0 0 3 0 2 4 0
|
|
0 4 0 1 0 2 5 0 0
|
|
7 0 0 2 0 0 1 6 8
|
|
0 3 0 0 0 0 7 2 4
|
|
8 0 0 0 0 6 0 0 5
|
|
*/
|
|
|
|
/*
|
|
0 0 0 0 3 7 0 0 0
|
|
0 0 8 0 0 2 5 0 3
|
|
0 2 0 8 4 0 0 0 0
|
|
0 0 6 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0 7
|
|
0 0 0 2 0 0 0 6 0
|
|
0 0 0 0 0 0 6 0 5
|
|
0 0 0 6 8 0 0 4 0
|
|
4 0 0 1 0 0 0 0 0
|
|
*/
|
|
__attribute_maybe_unused__ int grid_empty[N][N] = {
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
}
|