#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}
};
}