Compare commits

...

50 commits
0.1.0 ... main

Author SHA1 Message Date
fac2179c23
hopefully fix the paging issue 2025-04-17 09:43:20 -04:00
9b196f0b13
fix weird newline issue 2025-04-17 09:27:13 -04:00
be291628b8
fix weird interference with polls 2025-04-14 20:36:53 -04:00
881d3602e7
clean up I/O database 2025-04-12 09:44:14 -04:00
d0553e6575
redirect guide 2025-04-09 12:14:48 -04:00
6b5452c923
clarity; link to the releases page 2025-04-09 12:12:49 -04:00
5a6bb6e982
improve the guide 2025-04-07 11:04:24 -04:00
090d4d5d8d
i put this in the wrong place so now it doesn't actually reduce the size 2025-04-05 15:49:21 -04:00
1f70dd7000
ugh 2025-04-03 13:48:03 -04:00
bab758e93f
avoid duping polls 2025-04-03 13:46:08 -04:00
f55db7a7fd
fix polls randomly coiming back 2025-04-03 13:29:03 -04:00
988ac26e44
allow suggesting polls of elems you don't have, and allow suggestion correction 2025-04-03 11:49:48 -04:00
735915db89
strip useless debug symbols 2025-04-03 11:18:32 -04:00
e22c9d42da
fix 2025-04-02 20:37:18 -04:00
ed50d4daba
reverse path order 2025-04-02 20:32:44 -04:00
7a63fcb376
don't show paths of elements you don't have 2025-04-02 01:28:18 -04:00
053b04133a
fix paths 2025-04-01 22:00:06 -04:00
060963039c
fix weird skips 2025-04-01 21:14:53 -04:00
f1bc13d4e2
add paths 2025-04-01 21:10:25 -04:00
e6c412e661
reduce size allocated for blocks 2025-03-31 21:38:33 -04:00
d356b5423e
parse users differently 2025-03-30 02:43:52 -04:00
5b9254211f
improve paging logic, remove unused map code 2025-03-30 01:07:00 -04:00
242041da34
show inventory and poll totals 2025-03-29 21:47:26 -04:00
9dc0c0684e
proper paging 2025-03-29 05:00:37 -04:00
745a306b04
don't show polls that passed 2025-03-29 04:59:17 -04:00
177f9025e0
clean up the poll command output 2025-03-29 02:51:38 -04:00
4b3b6c9111
don't say anything when there is no combo 2025-03-28 16:37:38 -04:00
26770efc64
fix weird inv segfault 2025-03-28 02:31:01 -04:00
6ef90f930d
fix inv bug 2025-03-28 02:17:42 -04:00
79e380eddb
fix flushing bug 2025-03-28 01:54:00 -04:00
a6451e4904
don't continue on empty strings 2025-03-28 01:36:28 -04:00
c278da1705
add detailed help and polls commands 2025-03-27 22:12:19 -04:00
15f6ca9715
remove inv duping 2025-03-27 18:00:30 -04:00
fe1de151ff
optimization on memory 2025-03-27 15:56:47 -04:00
0e3dd1a83f
32-bit support, compile wrapper 2025-03-27 09:51:49 -04:00
dffdf200cc
optimize for filesize 2025-03-27 07:34:20 -04:00
aab88a6c19
typo simulator 2025-03-26 23:55:56 -04:00
cb2ec2873a
yes 2025-03-26 23:49:52 -04:00
1de63b1f14
some fixes to grammar 2025-03-26 23:47:21 -04:00
99bb41d542
add guide 2025-03-26 23:43:25 -04:00
0fd6e738d9
force elements to be lowercase 2025-03-26 22:15:43 -04:00
34d2d97c82
add suggestions 2025-03-26 21:19:01 -04:00
e6165f3af8
garbage collection 2025-03-26 01:20:38 -04:00
c02e8aa59a
attempt hotloading if element doesn't exist 2025-03-25 21:25:31 -04:00
8878053ee0
fix malformed combos 2025-03-25 20:56:40 -04:00
e6a00cf17b
add inventory paging, more elements 2025-03-25 20:19:51 -04:00
3baa7157dc
fix inv bug 2025-03-25 19:50:41 -04:00
d5e55b54ee
add inv saving, fix bugs 2025-03-25 19:48:55 -04:00
4bef1a220c
check both main dir and parent dir (temp fix) 2025-03-25 13:43:02 -04:00
f4be64fe19
fix compile bug on win32 2025-03-25 01:29:28 -04:00
14 changed files with 629 additions and 206 deletions

8
.gitignore vendored
View file

@ -1,2 +1,8 @@
/bin/elem
/bin/elem.exe
/bin/elem.exe
/bin/elem_32.exe
/inv.txt
/inv_users.txt
/polls.txt
/elem.tar.gz
/combos.txt

View file

@ -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

View file

@ -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).

View file

@ -9,3 +9,33 @@ wildfire;fire;fire
smoke;fire;water
puddle;water;water
elements;air;earth;fire;water
dirt;dust;earth
clay;dirt;wind
cobble;air;lava
stone;cobble;earth
gravel;cobble;wind
blue;fire;heat
steam;heat;water
energy;steam;steam
sky;air;wind
sky;air;cloud
space;air;sky
space;sky;sky
void;space;space
zero;energy;void
numbers;elements;zero
one;numbers;numbers
color;blue;numbers
spectrum;color;numbers
pond;earth;puddle
pond;puddle;puddle
lake;pond;water
lake;pond;pond
sea;lake;lake
sea;lake;water
ocean;sea;sea
ocean;land;sea
planet;earth;ocean
planet;land;ocean
star;fire;sky
pollution;air;dust

View file

@ -1,12 +1,41 @@
#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
// huge cleanup operation soon
struct pager {
int page;
int i;
int is_poll;
hashmap *cmd;
};
struct verifier {
char *name;
char *sugg;
};
struct succ {
char *sugg;
int *points;
};
long stoi(const char *s) {
long i;
i = 0;
while (*s >= '0' && *s <= '9') {
i = i * 10 + (*s - '0');
s++;
}
return i;
}
int fix_delim(char *command, char *needle) {
char *delim = strstr(command, needle);
@ -23,32 +52,303 @@ int sort_comp(const void *a, const void *b) {
return strcmp(*(char **)a, *(char **)b);
}
int inv_handler(const void *key, unsigned long size, unsigned long val,
void *usr) {
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 (key2[strlen(key2) - 1] == '\n') {
printf("- %s", (char *)key);
} else {
printf("- %s\n", (char *)key);
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 (strlen(key2) > 0 && key2[strlen(key2) - 1] == '\n') {
printf("- %s", key2);
} else {
printf("- %s\n", key2);
}
return 1;
}
int polls_handler(const void *key, size_t size, uintptr_t val, void *usr) {
struct verifier *verified = (struct verifier *)usr;
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, val3) == 0) {
free(val3);
return -1;
}
free(val3);
return 0;
}
int success_handler(const void *key, size_t size, uintptr_t val, void *usr) {
struct succ *verified = (struct succ *)usr;
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, val3) == 0) {
verified->points[0]++;
}
free(val3);
return 0;
}
char *handle_pages(char *command, char *invs) {
char *data;
char *data2;
if (strncmp(command, invs, strlen(invs)) == 0) {
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());
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;
}
hashmap_set(polls, key, strlen(key), (uintptr_t)val, 1);
point_thing++;
if (point_thing == UPVOTE_IN) {
printf("Poll was added into the game!\n");
FILE *fptr;
hashmap_set(combos, command_re, strlen(command_re), (uintptr_t)page, 0);
write_elements(combos, "../elem_data/" COMBO_FILE, 0);
return 1;
}
printf("Suggested %s = %s.\n", command_re, page);
// todo: clean old polls
FILE *fptr;
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;
}
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;
}
if (strcmp(command, "inv\n") == 0) {
printf("Your inventory:\n");
hashmap_iterate(inv, inv_handler, "inv");
}
printf("Your inventory (page %i):\n", page);
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;
}
@ -68,22 +368,19 @@ int get_command(char *command, char *command_re, char **sort_tmp) {
int cl = strlen(command);
if (cl < 2) return 0;
if (cl < 2)
return 0;
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++) {
combos = i;
sort_tmp[i] = strstr(sort_tmp[i - 1], ";");
if (sort_tmp[i] == 0 || sort_tmp[i] == sort_tmp[i-1])
if (sort_tmp[i] == 0 || sort_tmp[i] == sort_tmp[i - 1])
break;
if (strlen(sort_tmp[i]) < 2) {
if (strlen(sort_tmp[i]) < 2) {
sort_tmp[i][0] = '\0';
break;
}

View file

@ -1,3 +1,9 @@
#include "map.h"
int get_command(char *command, char *command_re, char **sort_tmp);
int slash_command(char *command, hashmap *inv);
int slash_command(char *command, hashmap *inv);
int suggest_command(char *command, char *command_re, hashmap *polls,
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);

View file

@ -4,31 +4,126 @@
#include <stdlib.h>
#include <string.h>
#define MAX_FILE_SIZE 1024 * 16
#include "main.h"
void 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");
if (fptr == NULL)
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))
str2 = calloc(MAX_FILE_SIZE, sizeof(char));
if (!fgets(str2, MAX_FILE_SIZE, fptr)) {
free(str2);
break;
}
if (use_inv) {
hashmap_set(m, str, strlen(str) - 2, (uintptr_t)1);
lines_get++;
str = calloc(strlen(str2) + 1, sizeof(char));
strcpy(str, str2);
free(str2);
did_something = 1;
if (mode == 1) {
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)1, 0);
continue;
}
char *combo_o = strstr(str, ";");
combo_o[0] = '\0';
combo_o++;
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;
}
char *combo = strstr(str, ";");
combo[0] = '\0';
combo++;
if (mode == 2 || mode == 3) {
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)combo, 1);
continue;
}
hashmap_set(m, combo, strlen(combo) - 1, (uintptr_t)str);
hashmap_set(m, combo, strlen(combo) - 1, (uintptr_t)str, 1);
}
if (!did_something) {
free(str);
}
fclose(fptr);
return did_something;
// todo: properly free this
}

View file

@ -1,2 +1,3 @@
#include "map.h"
void 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);

View file

@ -1,39 +1,113 @@
#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 MAX_BUF_LENGTH 1024
#define MAX_COMBO_LENGTH 1024
#include "main.h"
// todo: spacing in combos, clean init_tables
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(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(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);
}
int main(int argc, char *argv[]) {
char *name;
if (argc < 2) {
name = "guest";
} else {
name = argv[1];
}
hashmap *elements = hashmap_create();
hashmap *elements_rev = hashmap_create();
hashmap *already_done = hashmap_create();
hashmap *inv = hashmap_create();
hashmap *polls = hashmap_create();
char *command = calloc(MAX_BUF_LENGTH, sizeof(char));
char *command_re = calloc(MAX_BUF_LENGTH, sizeof(char));
char **sort_tmp = calloc(MAX_COMBO_LENGTH, sizeof(char **));
load_elements(elements, "bin/combos.txt", 0);
load_elements(inv, "bin/inv.txt", 1);
int was_combination = 0;
printf("Welcome to Elemental on Command Line!\n");
init_tables(elements, inv, polls, elements_rev, 1);
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);
int slashRan = slash_command(command,inv);
if (slashRan) continue;
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;
}
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;
hashmap_get(inv, sort_tmp[i], strlen(sort_tmp[i]) - 1, &result);
hashmap_get(inv, sort_tmp[i], strlen(sort_tmp[i]), &result);
if (result != 1) {
printf("You don't have %s.\n", sort_tmp[i]);
failed = 1;
@ -41,20 +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) {
printf("You didn't make anything.\n");
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) {
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;
hashmap_set(inv, res_str, strlen(res_str) - 1, (uintptr_t)1);
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);
write_elements(inv, INV_FILE, 1);
}
// free(command);

12
src/main.h Normal file
View 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);

106
src/map.c
View file

@ -2,9 +2,11 @@
// map.h
//
// Created by Mashpoe on 1/15/21.
// Modifications by BiglyDerv
//
#include "map.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -167,7 +169,8 @@ static struct bucket *find_entry(hashmap *m, const void *key, size_t ksize,
}
}
int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val) {
int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val,
int free_old) {
if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) {
if (hashmap_resize(m) == -1)
return -1;
@ -186,71 +189,16 @@ int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val) {
entry->ksize = ksize;
entry->hash = hash;
}
if (free_old && entry->value != 0 && entry->value != val &&
strcmp((char *)entry->value, (char *)val) == 0) {
free((char *)val);
return 0;
}
entry->value = 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);
@ -261,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) {

View file

@ -2,6 +2,7 @@
// map.h
//
// Created by Mashpoe on 1/15/21.
// Modifications by BiglyDerv
//
#ifndef map_h
@ -10,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>
@ -43,68 +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);
// 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_set(hashmap *map, const void *key, size_t ksize, uintptr_t value,
int free_old);
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
View 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