Compare commits
41 commits
Author | SHA1 | Date | |
---|---|---|---|
fac2179c23 | |||
9b196f0b13 | |||
be291628b8 | |||
881d3602e7 | |||
d0553e6575 | |||
6b5452c923 | |||
5a6bb6e982 | |||
090d4d5d8d | |||
1f70dd7000 | |||
bab758e93f | |||
f55db7a7fd | |||
988ac26e44 | |||
735915db89 | |||
e22c9d42da | |||
ed50d4daba | |||
7a63fcb376 | |||
053b04133a | |||
060963039c | |||
f1bc13d4e2 | |||
e6c412e661 | |||
d356b5423e | |||
5b9254211f | |||
242041da34 | |||
9dc0c0684e | |||
745a306b04 | |||
177f9025e0 | |||
4b3b6c9111 | |||
26770efc64 | |||
6ef90f930d | |||
79e380eddb | |||
a6451e4904 | |||
c278da1705 | |||
15f6ca9715 | |||
fe1de151ff | |||
0e3dd1a83f | |||
dffdf200cc | |||
aab88a6c19 | |||
cb2ec2873a | |||
1de63b1f14 | |||
99bb41d542 | |||
0fd6e738d9 |
12 changed files with 436 additions and 308 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,5 +1,8 @@
|
|||
/bin/elem
|
||||
/bin/elem.exe
|
||||
/bin/elem_32.exe
|
||||
/inv.txt
|
||||
/inv_users.txt
|
||||
/polls.txt
|
||||
/polls.txt
|
||||
/elem.tar.gz
|
||||
/combos.txt
|
2
Makefile
2
Makefile
|
@ -2,4 +2,4 @@ CC?=gcc
|
|||
BIN?=bin/elem
|
||||
|
||||
make: src/main.c
|
||||
$(CC) -o $(BIN) src/map.c src/loader.c src/command.c src/main.c
|
||||
$(CC) -ffunction-sections -fdata-sections -Wl,--gc-sections -Os -o $(BIN) src/map.c src/loader.c src/command.c src/main.c
|
16
README.md
16
README.md
|
@ -1,2 +1,14 @@
|
|||
# Elemental on CLI
|
||||
Elemental combination game written in C.
|
||||
# Elemental on Command Line
|
||||
This is an elemental combination game written in C, with optional multi-player support.
|
||||
|
||||
## Clients
|
||||
- [PenguinMod client](https://studio.penguinmod.com/?fps=200&clones=Infinity&offscreen&size=850x480#7218964246) for playing the game online with a fancy interface
|
||||
- [Discord client](https://discord.gg/DKZTWyWH3B) for playing the game publicly with other Discord members
|
||||
- [Web client](https://elem.dervland.net/) for a demo of the multiplayer version
|
||||
- [Original client](https://git.dervland.net/elemental/elemental-on-terminal/releases) for developers, administrators, and command line fans
|
||||
|
||||
## Single-player
|
||||
A single-player example pack is provided as an example in ``bin/combos.txt``. This can be customized by the user, although internal limitations require the combination string to be in alphabetical order.
|
||||
|
||||
## Multi-player
|
||||
To set up multi-player mode, use the guide with the [Web proxy](https://git.dervland.net/elemental/elemental-to-web).
|
330
src/command.c
330
src/command.c
|
@ -1,20 +1,20 @@
|
|||
#include "map.h"
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "loader.h"
|
||||
#include "main.h"
|
||||
|
||||
#define MAX_BUF_LENGTH 1024
|
||||
#define MAX_COMBO_LENGTH 1024
|
||||
#define poll_file "polls.txt"
|
||||
#define polls_lock_file "polls_lock.txt"
|
||||
#define combo_file "combos.txt"
|
||||
#define UPVOTE_IN 2
|
||||
// huge cleanup operation soon
|
||||
|
||||
struct pager {
|
||||
int page;
|
||||
int i;
|
||||
int is_poll;
|
||||
hashmap *cmd;
|
||||
};
|
||||
|
||||
struct verifier {
|
||||
|
@ -52,106 +52,153 @@ int sort_comp(const void *a, const void *b) {
|
|||
return strcmp(*(char **)a, *(char **)b);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)))
|
||||
|
||||
int inv_handler(const void *key, unsigned long size, unsigned long val,
|
||||
void *usr)
|
||||
#else
|
||||
int inv_handler(const void *key, unsigned long long size,
|
||||
unsigned long long val, void *usr)
|
||||
#endif
|
||||
{
|
||||
int inv_handler(const void *key, size_t size, uintptr_t val, void *usr) {
|
||||
struct pager *i = usr;
|
||||
if (val == 0)
|
||||
return 0;
|
||||
char *key2 = (char *)key;
|
||||
char *val2 = (char *)val;
|
||||
|
||||
if (i->is_poll) {
|
||||
char *key3 = calloc(strlen(key2) + 1, sizeof(char));
|
||||
memcpy(key3, key2, strstr(key2, "_") - key2);
|
||||
|
||||
char *val3 = strstr(val2, ";") + 1;
|
||||
uintptr_t result;
|
||||
|
||||
hashmap_get(i->cmd, val3, strlen(val3) - 1, &result);
|
||||
|
||||
if (result != 0) {
|
||||
free(key3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
i->i++;
|
||||
if (i->i < i->page * 10 || i->i >= (i->page + 1) * 10) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val2[strlen(val2) - 1] == '\n') {
|
||||
printf("- user:%s suggested %s", key3, val2);
|
||||
} else {
|
||||
printf("- user:%s suggested %s\n", key3, val2);
|
||||
}
|
||||
|
||||
free(key3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
i->i++;
|
||||
if (i->i < i->page * 10 || i->i >= (i->page + 1) * 10) {
|
||||
return 0;
|
||||
}
|
||||
if (val == 0)
|
||||
return 0;
|
||||
char *key2 = (char *)key;
|
||||
|
||||
if (key2[strlen(key2) - 1] == '\n') {
|
||||
printf("- %s", (char *)key);
|
||||
if (strlen(key2) > 0 && key2[strlen(key2) - 1] == '\n') {
|
||||
printf("- %s", key2);
|
||||
} else {
|
||||
printf("- %s\n", (char *)key);
|
||||
printf("- %s\n", key2);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)))
|
||||
|
||||
int polls_handler(const void *key, unsigned long size, unsigned long val,
|
||||
void *usr)
|
||||
#else
|
||||
int polls_handler(const void *key, unsigned long long size,
|
||||
unsigned long long val, void *usr)
|
||||
#endif
|
||||
{
|
||||
int polls_handler(const void *key, size_t size, uintptr_t val, void *usr) {
|
||||
struct verifier *verified = (struct verifier *)usr;
|
||||
|
||||
if (((char*) val)[strlen(((char *)val)) - 1] == '\n') {
|
||||
((char *)val)[strlen(((char *)val)) - 1] = '\0';
|
||||
char *val2 = (char *)val;
|
||||
char *val3 = malloc(strlen(val2) + 1);
|
||||
strcpy(val3, val2);
|
||||
|
||||
if (val3[strlen(val3) - 1] == '\n') {
|
||||
val3[strlen(val3) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (strncmp(verified->name, key, strlen(verified->name)) == 0 &&
|
||||
strcmp(verified->sugg, (char *)val) == 0)
|
||||
strcmp(verified->sugg, val3) == 0) {
|
||||
free(val3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(val3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)))
|
||||
|
||||
int success_handler(const void *key, unsigned long size, unsigned long val,
|
||||
void *usr)
|
||||
#else
|
||||
int success_handler(const void *key, unsigned long long size,
|
||||
unsigned long long val, void *usr)
|
||||
#endif
|
||||
{
|
||||
int success_handler(const void *key, size_t size, uintptr_t val, void *usr) {
|
||||
struct succ *verified = (struct succ *)usr;
|
||||
|
||||
if ((char *)val != verified->sugg) {
|
||||
//((char *)val)[strlen(((char *)val)) - 1] = '\0';
|
||||
char *val2 = (char *)val;
|
||||
char *val3 = malloc(strlen(val2) + 1);
|
||||
strcpy(val3, val2);
|
||||
|
||||
if (val3[strlen(val3) - 1] == '\n') {
|
||||
val3[strlen(val3) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (strcmp(verified->sugg, (char *)val) == 0) {
|
||||
if (strcmp(verified->sugg, val3) == 0) {
|
||||
verified->points[0]++;
|
||||
}
|
||||
|
||||
free(val3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int suggest_command(char *command, char *command_re, hashmap *polls,
|
||||
char *name) {
|
||||
if (command[0] == '/') {
|
||||
command++;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *invs = "suggest ";
|
||||
|
||||
char *page;
|
||||
|
||||
char *handle_pages(char *command, char *invs) {
|
||||
char *data;
|
||||
char *data2;
|
||||
if (strncmp(command, invs, strlen(invs)) == 0) {
|
||||
page = &command[strlen(invs)];
|
||||
data = &command[strlen(invs)];
|
||||
data2 = malloc(strlen(data) + 1);
|
||||
memcpy(data2,data,strlen(data) + 1);
|
||||
return data2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int handle_pages_int(char *command, char *invs) {
|
||||
if (strncmp(command, invs, strlen(invs)) == 0) {
|
||||
return stoi(&command[strlen(invs)]);
|
||||
} else if (strncmp(command, invs, strlen(invs) - 1) == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int suggest_command(char *command, char *command_re, hashmap *polls, hashmap *combos, char *name,
|
||||
int was_combination) {
|
||||
|
||||
char *page = handle_pages(command, "/suggest ");
|
||||
|
||||
if (!page)
|
||||
return 0;
|
||||
page[strlen(page) - 1] = '\0';
|
||||
|
||||
if (strstr(page, "\n") || strstr(page, ";") || strstr(page, ",") ||
|
||||
strstr(page, "+")) {
|
||||
printf("This element contains illegal characters.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *val = calloc(MAX_BUF_LENGTH, sizeof(char));
|
||||
sprintf(val, "%s;%s", page, command_re);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
char *key = calloc(MAX_BUF_LENGTH, sizeof(char));
|
||||
sprintf(key, "%s_%i", name, (int)rand());
|
||||
|
||||
struct verifier verified = {.name = name, .sugg = val};
|
||||
int point_thing = 0;
|
||||
|
||||
struct succ succer = {.sugg = val, .points = &point_thing};
|
||||
hashmap_iterate(polls, success_handler, (void *)&succer);
|
||||
|
||||
if (was_combination == 2 && point_thing == 0) {
|
||||
printf("You cannot create unique polls with elements you do not have.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct verifier verified = {.name = name, .sugg = val};
|
||||
if (hashmap_iterate(polls, polls_handler, (void *)&verified) == -1) {
|
||||
printf("You already suggested this!\n");
|
||||
return 1;
|
||||
|
@ -159,22 +206,15 @@ int suggest_command(char *command, char *command_re, hashmap *polls,
|
|||
|
||||
hashmap_set(polls, key, strlen(key), (uintptr_t)val, 1);
|
||||
|
||||
int point_thing = 0;
|
||||
|
||||
struct succ succer = {.sugg = val, .points = &point_thing};
|
||||
hashmap_iterate(polls, success_handler, (void *)&succer);
|
||||
point_thing++;
|
||||
|
||||
if (point_thing == UPVOTE_IN) {
|
||||
|
||||
printf("Poll was added into the game!\n");
|
||||
FILE *fptr;
|
||||
|
||||
fptr = fopen("../elem_data/" combo_file, "a");
|
||||
if (fptr == NULL)
|
||||
return 1;
|
||||
fwrite(val, sizeof(char), strlen(val), fptr);
|
||||
fwrite("\n", sizeof(char), 1, fptr);
|
||||
fclose(fptr);
|
||||
hashmap_set(combos, command_re, strlen(command_re), (uintptr_t)page, 0);
|
||||
|
||||
write_elements(combos, "../elem_data/" COMBO_FILE, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -184,41 +224,131 @@ int suggest_command(char *command, char *command_re, hashmap *polls,
|
|||
|
||||
FILE *fptr;
|
||||
|
||||
fptr = fopen("../elem_data/" poll_file, "a");
|
||||
if (fptr == NULL)
|
||||
write_elements(polls, "../elem_data/" POLL_FILE, 3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int help_command(char *command) {
|
||||
char *page = handle_pages(command, "/help");
|
||||
|
||||
if (!page)
|
||||
return 0;
|
||||
printf("Available "
|
||||
"commands:\n- elem1;elem2...\n- elem1+elem2...\n- elem1,elem2...\n- "
|
||||
"/help\n- /inv [page]\n- /suggest [combo]\n- /polls [page]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int polls_command(char *command, hashmap *polls, hashmap *cmd) {
|
||||
|
||||
int page = handle_pages_int(command, "/polls ");
|
||||
if (page == 0)
|
||||
return 0;
|
||||
|
||||
printf("Current polls (page %i):\n", page);
|
||||
struct pager i = {.page = page - 1, .i = -1, .is_poll = 1, .cmd = cmd};
|
||||
hashmap_iterate(polls, inv_handler, &i);
|
||||
printf("Total: %i\n", i.i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int path_command(char *command, hashmap *elements_rev, hashmap *already_done,
|
||||
int top, hashmap *inv) {
|
||||
char *page2;
|
||||
if (top) {
|
||||
page2 = handle_pages(command, "/path ");
|
||||
if (page2 == 0)
|
||||
return 0;
|
||||
} else {
|
||||
page2 = command;
|
||||
}
|
||||
|
||||
char *page = malloc(strlen(page2) + 1);
|
||||
strcpy(page, page2);
|
||||
if (page[strlen(page) - 1] == '\n') {
|
||||
page[strlen(page) - 1] = '\0';
|
||||
}
|
||||
if (strlen(page) == 0) {
|
||||
return 1;
|
||||
fwrite(key, sizeof(char), strlen(key), fptr);
|
||||
fwrite(";", sizeof(char), 1, fptr);
|
||||
fwrite(val, sizeof(char), strlen(val), fptr);
|
||||
fwrite("\n", sizeof(char), 1, fptr);
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
if (top) {
|
||||
printf("Path of %s:\n", page);
|
||||
hashmap_free(already_done);
|
||||
already_done = hashmap_create();
|
||||
}
|
||||
|
||||
uintptr_t result;
|
||||
hashmap_get(inv, page, strlen(page), &result);
|
||||
|
||||
if (top && result != 1) {
|
||||
printf("You don't have %s.\n", page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hashmap_get(already_done, page, strlen(page), &result);
|
||||
|
||||
if (result != 0) {
|
||||
return 1;
|
||||
}
|
||||
hashmap_set(already_done, page, strlen(page), (uintptr_t)1, 0);
|
||||
|
||||
hashmap_get(elements_rev, page, strlen(page) - 1, &result);
|
||||
|
||||
if (result == 0 || strlen((char *)result) < 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// todo: refactor;
|
||||
char *tmp = (char *)result, *tmp2 = 0;
|
||||
if (tmp[strlen(tmp) - 1] == '\n') {
|
||||
tmp[strlen(tmp) - 1] = '\0';
|
||||
}
|
||||
|
||||
int combos = 0;
|
||||
for (int i = 1; i < MAX_COMBO_LENGTH; i++) {
|
||||
combos = i;
|
||||
tmp2 = tmp;
|
||||
if (tmp2[0] == ';') {
|
||||
tmp2++;
|
||||
}
|
||||
tmp = strstr(tmp2, ";");
|
||||
if (tmp == 0) {
|
||||
tmp = &tmp2[strlen(tmp2)];
|
||||
}
|
||||
|
||||
char *tmp23 = malloc(strlen(tmp2) - strlen(tmp) + 2);
|
||||
memcpy(tmp23, &tmp2[0], strlen(tmp2) - strlen(tmp) + 2);
|
||||
tmp23[strlen(tmp2) - strlen(tmp) + 1] = '\0';
|
||||
char *strstrd = strstr(tmp23, ";");
|
||||
if (strstrd != 0) {
|
||||
strstrd[0] = '\0';
|
||||
}
|
||||
|
||||
path_command(tmp23, elements_rev, already_done, 0, inv);
|
||||
|
||||
if (strlen(tmp23) < 1) {
|
||||
break;
|
||||
}
|
||||
free(tmp23);
|
||||
}
|
||||
|
||||
printf("<- %s\n-> %s\n", (char *)result, (char *)page);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int slash_command(char *command, hashmap *inv) {
|
||||
|
||||
if (command[0] == '/') {
|
||||
command++;
|
||||
} else {
|
||||
int page = handle_pages_int(command, "/inv ");
|
||||
if (page == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *invs = "inv ";
|
||||
|
||||
int page;
|
||||
|
||||
if (strncmp(command, invs, strlen(invs)) == 0) {
|
||||
page = stoi(&command[strlen(invs)]);
|
||||
} else if (strncmp(command, invs, strlen(invs) - 1) == 0) {
|
||||
page = 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Your inventory (page %i):\n", page);
|
||||
struct pager i = {.page = page - 1, .i = -1};
|
||||
struct pager i = {.page = page - 1, .i = -1, .is_poll = 0};
|
||||
hashmap_iterate(inv, inv_handler, &i);
|
||||
printf("Total: %i\n", i.i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -243,10 +373,6 @@ int get_command(char *command, char *command_re, char **sort_tmp) {
|
|||
|
||||
command[cl - 1] = '\0';
|
||||
|
||||
for (int i = 0; i < cl - 1; i++) {
|
||||
command[i] = tolower(command[i]);
|
||||
}
|
||||
|
||||
sort_tmp[0] = command;
|
||||
int combos = 0;
|
||||
for (int i = 1; i < MAX_COMBO_LENGTH; i++) {
|
||||
|
|
|
@ -2,4 +2,8 @@
|
|||
int get_command(char *command, char *command_re, char **sort_tmp);
|
||||
int slash_command(char *command, hashmap *inv);
|
||||
int suggest_command(char *command, char *command_re, hashmap *polls,
|
||||
char *name);
|
||||
hashmap *combos, char *name, int was_combination);
|
||||
int help_command(char *command);
|
||||
int polls_command(char *command, hashmap *polls, hashmap *cmd);
|
||||
int path_command(char *command, hashmap *elements_rev, hashmap *already_done,
|
||||
int top, hashmap *inv);
|
95
src/loader.c
95
src/loader.c
|
@ -4,9 +4,60 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_FILE_SIZE 1024 * 16
|
||||
#include "main.h"
|
||||
|
||||
int load_elements(hashmap *m, char *table, int use_inv) {
|
||||
struct write_struct {
|
||||
FILE *fptr;
|
||||
int mode;
|
||||
};
|
||||
|
||||
int entry_handler(const void *key, size_t size, uintptr_t val, void *usr) {
|
||||
struct write_struct *usr2 = (struct write_struct *)usr;
|
||||
|
||||
char *val2 = (char *)val;
|
||||
char *key2 = (char *)key;
|
||||
|
||||
if (usr2->mode == 0) {
|
||||
fwrite(val2, sizeof(char), strlen(val2), usr2->fptr);
|
||||
fwrite(";", sizeof(char), 1, usr2->fptr);
|
||||
fwrite(key2, sizeof(char), strlen(key2), usr2->fptr);
|
||||
if (key2[strlen(key2) - 1] != '\n') {
|
||||
fwrite("\n", sizeof(char), 1, usr2->fptr);
|
||||
}
|
||||
} else if (usr2->mode == 1) {
|
||||
fwrite(key2, sizeof(char), strlen(key2), usr2->fptr);
|
||||
if (key2[strlen(key2) - 1] != '\n') {
|
||||
fwrite("\n", sizeof(char), 1, usr2->fptr);
|
||||
}
|
||||
} else {
|
||||
fwrite(key2, sizeof(char), strlen(key2), usr2->fptr);
|
||||
fwrite(";", sizeof(char), 1, usr2->fptr);
|
||||
fwrite(val2, sizeof(char), strlen(val2), usr2->fptr);
|
||||
if (val2[strlen(val2) - 1] != '\n') {
|
||||
fwrite("\n", sizeof(char), 1, usr2->fptr);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void write_elements(hashmap *m, char *table, int mode) {
|
||||
FILE *fptr;
|
||||
|
||||
fptr = fopen(table, "w");
|
||||
|
||||
if (fptr == NULL)
|
||||
return;
|
||||
|
||||
struct write_struct writer = {.fptr = fptr, .mode = mode};
|
||||
|
||||
hashmap_iterate(m, entry_handler, (void *)&writer);
|
||||
//fwrite("\n", sizeof(char), 1, fptr);
|
||||
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
int load_elements(hashmap *m, char *table, int mode) {
|
||||
FILE *fptr;
|
||||
|
||||
fptr = fopen(table, "r");
|
||||
|
@ -15,29 +66,51 @@ int load_elements(hashmap *m, char *table, int use_inv) {
|
|||
return 0;
|
||||
|
||||
char *str;
|
||||
char *str2;
|
||||
|
||||
int did_something = 0;
|
||||
|
||||
int lines_get = 0;
|
||||
|
||||
while (1) {
|
||||
str = calloc(MAX_FILE_SIZE, sizeof(char));
|
||||
if (!fgets(str, MAX_FILE_SIZE, fptr)) {
|
||||
free(str);
|
||||
str2 = calloc(MAX_FILE_SIZE, sizeof(char));
|
||||
if (!fgets(str2, MAX_FILE_SIZE, fptr)) {
|
||||
free(str2);
|
||||
break;
|
||||
}
|
||||
|
||||
lines_get++;
|
||||
|
||||
str = calloc(strlen(str2) + 1, sizeof(char));
|
||||
strcpy(str, str2);
|
||||
free(str2);
|
||||
|
||||
did_something = 1;
|
||||
|
||||
if (use_inv == 1) {
|
||||
if (mode == 1) {
|
||||
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)1, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *combo = strstr(str, ";");
|
||||
combo[0] = '\0';
|
||||
combo++;
|
||||
char *combo_o = strstr(str, ";");
|
||||
combo_o[0] = '\0';
|
||||
combo_o++;
|
||||
|
||||
if (use_inv == 2) {
|
||||
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)combo, 0);
|
||||
char *combo = calloc(strlen(combo_o) + 1, sizeof(char));
|
||||
strcpy(combo, combo_o);
|
||||
|
||||
if (mode == 3) {
|
||||
uintptr_t result;
|
||||
hashmap_get(m, str, strlen(str) - 1, &result);
|
||||
|
||||
char *res = (char *)result;
|
||||
|
||||
if (res != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode == 2 || mode == 3) {
|
||||
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)combo, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
#include "map.h"
|
||||
int load_elements(hashmap *m, char *table, int use_inv);
|
||||
int load_elements(hashmap *m, char *table, int mode);
|
||||
void write_elements(hashmap *m, char *table, int mode);
|
118
src/main.c
118
src/main.c
|
@ -1,34 +1,36 @@
|
|||
#include "command.h"
|
||||
#include "loader.h"
|
||||
#include "map.h"
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define inv_file "inv.txt"
|
||||
#define inv_base_file "inv_base.txt"
|
||||
#define combo_file "combos.txt"
|
||||
#define poll_file "polls.txt"
|
||||
#define polls_lock_file "polls_lock.txt"
|
||||
#include "main.h"
|
||||
|
||||
#define MAX_BUF_LENGTH 1024
|
||||
#define MAX_COMBO_LENGTH 1024
|
||||
// todo: spacing in combos, clean init_tables
|
||||
|
||||
void init_tables(hashmap *elements, hashmap *inv, hashmap *polls, int do_inv) {
|
||||
load_elements(elements, "../elem_data/" combo_file, 0);
|
||||
load_elements(elements, combo_file, 0) ||
|
||||
load_elements(elements, "bin/" combo_file, 0);
|
||||
void init_tables(hashmap *elements, hashmap *inv, hashmap *polls,
|
||||
hashmap *elements_rev, int do_inv) {
|
||||
load_elements(elements, "../elem_data/" COMBO_FILE, 0);
|
||||
load_elements(elements, COMBO_FILE, 0) ||
|
||||
load_elements(elements, "bin/" COMBO_FILE, 0);
|
||||
|
||||
load_elements(polls, "../elem_data/" poll_file, 2);
|
||||
load_elements(elements_rev, "../elem_data/" COMBO_FILE, 3);
|
||||
load_elements(elements_rev, COMBO_FILE, 3) ||
|
||||
load_elements(elements_rev, "bin/" COMBO_FILE, 3);
|
||||
|
||||
load_elements(polls, "../elem_data/" POLL_FILE, 3);
|
||||
|
||||
if (!do_inv)
|
||||
return;
|
||||
|
||||
load_elements(elements, "../elem_data/" inv_base_file, 1) ||
|
||||
load_elements(inv, inv_base_file, 1) ||
|
||||
load_elements(inv, "bin/" inv_base_file, 1);
|
||||
load_elements(inv, "../elem_data/" INV_BASE_FILE, 1) ||
|
||||
load_elements(inv, INV_BASE_FILE, 1) ||
|
||||
load_elements(inv, "bin/" INV_BASE_FILE, 1);
|
||||
|
||||
load_elements(inv, inv_file, 1);
|
||||
load_elements(inv, INV_FILE, 1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
@ -40,6 +42,8 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
hashmap *elements = hashmap_create();
|
||||
hashmap *elements_rev = hashmap_create();
|
||||
hashmap *already_done = hashmap_create();
|
||||
hashmap *inv = hashmap_create();
|
||||
hashmap *polls = hashmap_create();
|
||||
|
||||
|
@ -47,28 +51,59 @@ int main(int argc, char *argv[]) {
|
|||
char *command_re = calloc(MAX_BUF_LENGTH, sizeof(char));
|
||||
char **sort_tmp = calloc(MAX_COMBO_LENGTH, sizeof(char **));
|
||||
|
||||
int wasCombination = 0;
|
||||
int was_combination = 0;
|
||||
|
||||
init_tables(elements, inv, polls, 1);
|
||||
init_tables(elements, inv, polls, elements_rev, 1);
|
||||
|
||||
printf("%s, welcome to Elemental on Command Line!\n", name);
|
||||
printf("user:%s, welcome to Elemental on Command Line!\nType /help for "
|
||||
"commands.\n",
|
||||
name);
|
||||
int newline = 1;
|
||||
while (1) {
|
||||
// todo: separate into functions
|
||||
fflush(stdout);
|
||||
|
||||
printf("\n");
|
||||
if (newline) {
|
||||
printf("\n");
|
||||
}
|
||||
fgets(command, MAX_BUF_LENGTH - 1, stdin);
|
||||
|
||||
if (wasCombination && suggest_command(command, command_re, polls, name)) {
|
||||
wasCombination = 0;
|
||||
int cl = strlen(command);
|
||||
|
||||
if (cl < 2) {
|
||||
newline = 0;
|
||||
continue;
|
||||
} else {
|
||||
newline = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cl - 1; i++) {
|
||||
command[i] = tolower(command[i]);
|
||||
}
|
||||
|
||||
if (was_combination &&
|
||||
suggest_command(command, command_re, polls, elements, name, was_combination)) {
|
||||
continue;
|
||||
}
|
||||
wasCombination = 0;
|
||||
|
||||
was_combination = 0;
|
||||
if (help_command(command))
|
||||
continue;
|
||||
if (polls_command(command, polls, elements)) {
|
||||
hashmap_free(polls);
|
||||
polls = hashmap_create();
|
||||
init_tables(elements, inv, polls, elements_rev, 0);
|
||||
}
|
||||
if (slash_command(command, inv))
|
||||
continue;
|
||||
|
||||
if (path_command(command, elements_rev, already_done, 1, inv))
|
||||
continue;
|
||||
|
||||
int combos = get_command(command, command_re, sort_tmp);
|
||||
|
||||
if (combos < 2)
|
||||
continue;
|
||||
|
||||
int failed = 0;
|
||||
for (int i = 0; i < combos; i++) {
|
||||
uintptr_t result;
|
||||
|
@ -80,37 +115,44 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
continue;
|
||||
|
||||
uintptr_t result;
|
||||
hashmap_get(elements, command_re, strlen(command_re), &result);
|
||||
|
||||
if (result == 0) {
|
||||
init_tables(elements, inv, polls, 0);
|
||||
init_tables(elements, inv, polls, elements_rev, 0);
|
||||
|
||||
hashmap_get(elements, command_re, strlen(command_re), &result);
|
||||
}
|
||||
|
||||
if (result == 0 && failed) {
|
||||
was_combination = 2;
|
||||
printf("Use /suggest to upvote a pre-existing combination.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
wasCombination = 1;
|
||||
was_combination = 1;
|
||||
printf("You didn't make anything; use /suggest to suggest an element.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failed)
|
||||
continue;
|
||||
|
||||
char *res_str = (char *)result;
|
||||
|
||||
uintptr_t result2;
|
||||
|
||||
hashmap_get(inv, res_str, strlen(res_str), &result2);
|
||||
|
||||
if (result2 == 1) {
|
||||
printf("You made %s, but you already have it.\n", res_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
hashmap_set(inv, res_str, strlen(res_str), (uintptr_t)1, 0);
|
||||
printf("You made %s!\n", res_str);
|
||||
|
||||
FILE *fptr;
|
||||
|
||||
fptr = fopen(inv_file, "a");
|
||||
if (fptr == NULL)
|
||||
continue;
|
||||
fwrite(res_str, sizeof(char), strlen(res_str), fptr);
|
||||
fwrite("\n", sizeof(char), 1, fptr);
|
||||
fclose(fptr);
|
||||
write_elements(inv, INV_FILE, 1);
|
||||
}
|
||||
|
||||
// free(command);
|
||||
|
|
12
src/main.h
Normal file
12
src/main.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#define INV_FILE "inv.txt"
|
||||
#define INV_BASE_FILE "inv_base.txt"
|
||||
#define COMBO_FILE "combos.txt"
|
||||
#define POLL_FILE "polls.txt"
|
||||
#define POLLS_LOCK_FILE "polls_lock.txt"
|
||||
#define LB_FILE "lb.txt"
|
||||
|
||||
#define MAX_BUF_LENGTH 1024
|
||||
#define MAX_COMBO_LENGTH 1024
|
||||
#define UPVOTE_IN 2
|
||||
#define MAX_FILE_SIZE 1024
|
||||
long stoi(const char *s);
|
95
src/map.c
95
src/map.c
|
@ -199,67 +199,6 @@ int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_get_set(hashmap *m, const void *key, size_t ksize,
|
||||
uintptr_t *out_in) {
|
||||
if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) {
|
||||
if (hashmap_resize(m) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t hash = hash_data(key, ksize);
|
||||
struct bucket *entry = find_entry(m, key, ksize, hash);
|
||||
if (entry->key == NULL) {
|
||||
m->last->next = entry;
|
||||
m->last = entry;
|
||||
entry->next = NULL;
|
||||
|
||||
++m->count;
|
||||
|
||||
entry->value = *out_in;
|
||||
entry->key = key;
|
||||
entry->ksize = ksize;
|
||||
entry->hash = hash;
|
||||
|
||||
return 0;
|
||||
}
|
||||
*out_in = entry->value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hashmap_set_free(hashmap *m, const void *key, size_t ksize, uintptr_t val,
|
||||
hashmap_callback c, void *usr) {
|
||||
if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) {
|
||||
if (hashmap_resize(m) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t hash = hash_data(key, ksize);
|
||||
struct bucket *entry = find_entry(m, key, ksize, hash);
|
||||
if (entry->key == NULL) {
|
||||
m->last->next = entry;
|
||||
m->last = entry;
|
||||
entry->next = NULL;
|
||||
|
||||
++m->count;
|
||||
|
||||
entry->key = key;
|
||||
entry->ksize = ksize;
|
||||
entry->hash = hash;
|
||||
entry->value = val;
|
||||
// there was no overwrite, exit the function.
|
||||
return 0;
|
||||
}
|
||||
// allow the callback to free entry data.
|
||||
// use old key and value so the callback can free them.
|
||||
// the old key and value will be overwritten after this call.
|
||||
int error = c(entry->key, ksize, entry->value, usr);
|
||||
|
||||
// overwrite the old key pointer in case the callback frees it.
|
||||
entry->key = key;
|
||||
entry->value = val;
|
||||
return error;
|
||||
}
|
||||
|
||||
int hashmap_get(hashmap *m, const void *key, size_t ksize, uintptr_t *out_val) {
|
||||
uint32_t hash = hash_data(key, ksize);
|
||||
struct bucket *entry = find_entry(m, key, ksize, hash);
|
||||
|
@ -270,40 +209,6 @@ int hashmap_get(hashmap *m, const void *key, size_t ksize, uintptr_t *out_val) {
|
|||
return entry->key != NULL ? 1 : 0;
|
||||
}
|
||||
|
||||
// doesn't "remove" the element per se, but it will be ignored.
|
||||
// the element will eventually be removed when the map is resized.
|
||||
void hashmap_remove(hashmap *m, const void *key, size_t ksize) {
|
||||
uint32_t hash = hash_data(key, ksize);
|
||||
struct bucket *entry = find_entry(m, key, ksize, hash);
|
||||
|
||||
if (entry->key != NULL) {
|
||||
|
||||
// "tombstone" entry is signified by a NULL key with a nonzero value
|
||||
// element removal is optional because of the overhead of tombstone checks
|
||||
entry->key = NULL;
|
||||
entry->value = 0xDEAD; // I mean, it's a tombstone...
|
||||
|
||||
++m->tombstone_count;
|
||||
}
|
||||
}
|
||||
|
||||
void hashmap_remove_free(hashmap *m, const void *key, size_t ksize,
|
||||
hashmap_callback c, void *usr) {
|
||||
uint32_t hash = hash_data(key, ksize);
|
||||
struct bucket *entry = find_entry(m, key, ksize, hash);
|
||||
|
||||
if (entry->key != NULL) {
|
||||
c(entry->key, entry->ksize, entry->value, usr);
|
||||
|
||||
// "tombstone" entry is signified by a NULL key with a nonzero value
|
||||
// element removal is optional because of the overhead of tombstone checks
|
||||
entry->key = NULL;
|
||||
entry->value = 0xDEAD; // I mean, it's a tombstone...
|
||||
|
||||
++m->tombstone_count;
|
||||
}
|
||||
}
|
||||
|
||||
int hashmap_size(hashmap *m) { return m->count - m->tombstone_count; }
|
||||
|
||||
int hashmap_iterate(hashmap *m, hashmap_callback c, void *user_ptr) {
|
||||
|
|
56
src/map.h
56
src/map.h
|
@ -11,10 +11,6 @@
|
|||
#define hashmap_str_lit(str) (str), sizeof(str) - 1
|
||||
#define hashmap_static_arr(arr) (arr), sizeof(arr)
|
||||
|
||||
// removal of map elements is disabled by default because of its slight
|
||||
// overhead. if you want to enable this feature, uncomment the line below:
|
||||
// #define __HASHMAP_REMOVABLE
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -44,69 +40,17 @@ struct hashmap {
|
|||
struct bucket *last;
|
||||
};
|
||||
|
||||
// hashmaps can associate keys with pointer values or integral types.
|
||||
typedef struct hashmap hashmap;
|
||||
|
||||
// a callback type used for iterating over a map/freeing entries:
|
||||
// `int <function name>(const void* key, size_t size, uintptr_t value, void*
|
||||
// usr)` `usr` is a user pointer which can be passed through `hashmap_iterate`.
|
||||
typedef int (*hashmap_callback)(const void *key, size_t ksize, uintptr_t value,
|
||||
void *usr);
|
||||
|
||||
hashmap *hashmap_create(void);
|
||||
|
||||
// only frees the hashmap object and buckets.
|
||||
// does not call free on each element's `key` or `value`.
|
||||
// to free data associated with an element, call `hashmap_iterate`.
|
||||
void hashmap_free(hashmap *map);
|
||||
|
||||
// does not make a copy of `key`.
|
||||
// you must copy it yourself if you want to guarantee its lifetime,
|
||||
// or if you intend to call `hashmap_key_free`.
|
||||
// returns -1 on error.
|
||||
int hashmap_set(hashmap *map, const void *key, size_t ksize, uintptr_t value,
|
||||
int free_old);
|
||||
|
||||
// adds an entry if it doesn't exist, using the value of `*out_in`.
|
||||
// if it does exist, it sets value in `*out_in`, meaning the value
|
||||
// of the entry will be in `*out_in` regardless of whether or not
|
||||
// it existed in the first place.
|
||||
// returns -1 on error.
|
||||
// returns 1 if the entry already existed, returns 0 otherwise.
|
||||
int hashmap_get_set(hashmap *map, const void *key, size_t ksize,
|
||||
uintptr_t *out_in);
|
||||
|
||||
// similar to `hashmap_set()`, but when overwriting an entry,
|
||||
// you'll be able properly free the old entry's data via a callback.
|
||||
// unlike `hashmap_set()`, this function will overwrite the original key
|
||||
// pointer, which means you can free the old key in the callback if applicable.
|
||||
int hashmap_set_free(hashmap *map, const void *key, size_t ksize,
|
||||
uintptr_t value, hashmap_callback c, void *usr);
|
||||
|
||||
int hashmap_get(hashmap *map, const void *key, size_t ksize,
|
||||
uintptr_t *out_val);
|
||||
|
||||
#ifdef __HASHMAP_REMOVABLE
|
||||
void hashmap_remove(hashmap *map, const void *key, size_t ksize);
|
||||
|
||||
// same as `hashmap_remove()`, but it allows you to free an entry's data first
|
||||
// via a callback.
|
||||
void hashmap_remove_free(hashmap *m, const void *key, size_t ksize,
|
||||
hashmap_callback c, void *usr);
|
||||
#endif
|
||||
|
||||
int hashmap_size(hashmap *map);
|
||||
|
||||
// iterate over the map, calling `c` on every element.
|
||||
// goes through elements in the order they were added.
|
||||
// the element's key, key size, value, and `usr` will be passed to `c`.
|
||||
// if `c` returns -1 the iteration is aborted.
|
||||
// returns the last result of `c`
|
||||
int hashmap_iterate(hashmap *map, hashmap_callback c, void *usr);
|
||||
|
||||
// dumps bucket info for debugging.
|
||||
// allows you to see how many collisions you are getting.
|
||||
// `0` is an empty bucket, `1` is occupied, and `x` is removed.
|
||||
// void bucket_dump(hashmap *m);
|
||||
|
||||
#endif // map_h
|
||||
|
|
6
wrap.sh
Executable file
6
wrap.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
make
|
||||
CC=x86_64-w64-mingw32-gcc BIN=bin/elem.exe make
|
||||
CC=i686-w64-mingw32-gcc BIN=bin/elem_32.exe make
|
||||
strip --strip-all bin/elem
|
||||
rm elem.tar.gz
|
||||
tar -cvf elem.tar.gz bin
|
Loading…
Reference in a new issue