mirror of https://github.com/dirtbags/moth.git
cowd which runs
This commit is contained in:
parent
dfd354b863
commit
b475e5330a
|
@ -0,0 +1,58 @@
|
||||||
|
The Cow Game
|
||||||
|
============
|
||||||
|
|
||||||
|
You are trying to guess a 4-nybble sequence. Each nybble will have
|
||||||
|
either 1 or 2 bits set, and the highest bit will never be set. The
|
||||||
|
game server will tell you how many nybbles in each guess were correct,
|
||||||
|
and how many had one correct bit. It does not tell you which
|
||||||
|
positions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The Cow Client
|
||||||
|
==============
|
||||||
|
|
||||||
|
The client connects to the Cow server running on the IPv6 address
|
||||||
|
provided in argument 1. If argument 2 is present, the client will
|
||||||
|
try to run it, providing stdin and stdout as in interactive mode.
|
||||||
|
|
||||||
|
In interactive mode (no argument 2), the client reads a guess in the
|
||||||
|
form of 4 ASCII numerals, and prints the number of correct nybbles
|
||||||
|
followed by the number of nybbles with one correct bit.
|
||||||
|
|
||||||
|
Here is an example of a session:
|
||||||
|
|
||||||
|
1111
|
||||||
|
12
|
||||||
|
2222
|
||||||
|
10
|
||||||
|
4444
|
||||||
|
02
|
||||||
|
4244
|
||||||
|
12
|
||||||
|
1244
|
||||||
|
22
|
||||||
|
1255
|
||||||
|
cow:xylep-radar-nanox
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The Cow Protocol
|
||||||
|
================
|
||||||
|
|
||||||
|
cowd runs on port 3782.
|
||||||
|
|
||||||
|
The client always sends 6 octets. To request a new session, it sends
|
||||||
|
all zeroes. Otherwise it sends the 4-octet game identifier provided
|
||||||
|
by the server, concatenated with a 2-octet guess.
|
||||||
|
|
||||||
|
The server will respond with a new game identifier (4 octets) to a new
|
||||||
|
game request or if the game requested is too old. If a guess is
|
||||||
|
incorrect, the server will respond with either a 1-octet score in
|
||||||
|
which the high nybble is the number of correct nybbles in the guess,
|
||||||
|
and the low nybble is the number of nybbles in the guess with one
|
||||||
|
correct bit. If a guess is correct, the server will respond with a
|
||||||
|
token of length 5 octets or more.
|
||||||
|
|
||||||
|
There are multiple tokens, one per number of guesses used, up to
|
||||||
|
some maximum number of guesses defined per server instance.
|
|
@ -1,13 +1,143 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/time.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#define TIMEOUT 30
|
||||||
|
|
||||||
|
#define NTOKENS 20
|
||||||
|
#define TOKENLEN 50
|
||||||
|
char tokens[NTOKENS][TOKENLEN];
|
||||||
|
int ntokens;
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
time_t death;
|
||||||
|
uint16_t answer;
|
||||||
|
uint16_t guesses;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NSTATES 500
|
||||||
|
struct state states[NSTATES] = { 0 };
|
||||||
|
|
||||||
|
int
|
||||||
|
bind_port(struct in6_addr *addr, int fd, uint16_t port)
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 saddr = { 0 };
|
||||||
|
|
||||||
|
saddr.sin6_family = AF_INET6;
|
||||||
|
saddr.sin6_port = htons(port);
|
||||||
|
memcpy(&saddr.sin6_addr, addr, sizeof *addr);
|
||||||
|
return bind(fd, (struct sockaddr *) &saddr, sizeof saddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct newgame {
|
||||||
|
uint16_t offset;
|
||||||
|
uint16_t token;
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
mungle(char *str, int len)
|
new_game(int sock, time_t now, struct sockaddr_in6 *from,
|
||||||
|
socklen_t fromlen)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct newgame g;
|
||||||
|
|
||||||
for (i = 0; i < len; i += 1) {
|
for (g.offset = 0; g.offset < NSTATES; g.offset += 1) {
|
||||||
str[i] ^= 0xff;
|
struct state *s = &states[g.offset];
|
||||||
|
|
||||||
|
if (s->death < now) {
|
||||||
|
s->death = now + TIMEOUT;
|
||||||
|
s->guesses = 0;
|
||||||
|
s->answer = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i += 1) {
|
||||||
|
s->answer = (s->answer << 4) | ((random() % 6) + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g.offset < NSTATES) {
|
||||||
|
sendto(sock, &g, sizeof(g), 0, (struct sockaddr *) from, fromlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct guess {
|
||||||
|
uint16_t offset;
|
||||||
|
uint16_t token;
|
||||||
|
uint16_t guess;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
loop(int sock)
|
||||||
|
{
|
||||||
|
struct guess g;
|
||||||
|
struct state *cur;
|
||||||
|
struct sockaddr_in6 from;
|
||||||
|
socklen_t fromlen = sizeof from;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read guess
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
ssize_t inlen;
|
||||||
|
|
||||||
|
inlen = recvfrom(sock, &g, sizeof g, 0,
|
||||||
|
(struct sockaddr *) &from, &fromlen);
|
||||||
|
if (inlen != sizeof g) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bounds check
|
||||||
|
*/
|
||||||
|
if (g.offset >= NSTATES) {
|
||||||
|
g.offset = 0;
|
||||||
|
}
|
||||||
|
cur = &states[g.offset];
|
||||||
|
|
||||||
|
if ((g.token != cur->answer) || /* Wrong token? */
|
||||||
|
(cur->death < now) || /* Old game? */
|
||||||
|
(cur->guesses++ > 100)) { /* Too dumb? */
|
||||||
|
/*
|
||||||
|
* Start a new game
|
||||||
|
*/
|
||||||
|
new_game(sock, now, &from, fromlen);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
uint8_t reply;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i += 1) {
|
||||||
|
int s = (g.guess >> (i * 4)) & 0xf;
|
||||||
|
int a = (cur->answer >> (i * 4)) & 0xf;
|
||||||
|
if ((s < 1) || (s > 7)) {
|
||||||
|
reply = 0;
|
||||||
|
break;
|
||||||
|
} else if (s == a) {
|
||||||
|
reply += 0x10;
|
||||||
|
} else if (s & a) {
|
||||||
|
reply += 0x01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply == 0x40) {
|
||||||
|
if (cur->guesses > ntokens) {
|
||||||
|
sendto(sock, tokens[cur->guesses],
|
||||||
|
strlen(tokens[cur->guesses]), 0,
|
||||||
|
(struct sockaddr *) &from, fromlen);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendto(sock, &reply, sizeof reply, 0, (struct sockaddr *) &from,
|
||||||
|
fromlen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +145,39 @@ int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
long answer = 0;
|
long answer = 0;
|
||||||
|
int sock;
|
||||||
int i;
|
int i;
|
||||||
|
struct in6_addr addr;
|
||||||
|
|
||||||
{
|
srand(time(NULL));
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
if (argc > 1) {
|
||||||
srandom(tv.tv_usec);
|
if (0 >= inet_pton(AF_INET6, argv[1], &addr)) {
|
||||||
|
fprintf(stderr, "invalid address: %s\n", argv[1]);
|
||||||
|
return EX_IOERR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(&addr, &in6addr_any, sizeof addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read in tokens
|
||||||
|
*/
|
||||||
|
for (ntokens = 0; ntokens < NTOKENS; ntokens += 1) {
|
||||||
|
if (NULL == fgets(tokens[ntokens], TOKENLEN, stdin)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Read %d tokens.\n", ntokens);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up socket
|
||||||
|
*/
|
||||||
|
sock = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
i = bind_port(&addr, sock, 3782);
|
||||||
|
if (-1 == i) {
|
||||||
|
perror("Bind port 3782");
|
||||||
|
return EX_IOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 4; i += 1) {
|
for (i = 0; i < 4; i += 1) {
|
||||||
|
@ -29,31 +185,7 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
char line[20];
|
loop(sock);
|
||||||
long guess;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (NULL == fgets(line, sizeof(line), stdin)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
guess = strtol(line, NULL, 16);
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i += 1) {
|
|
||||||
int g = (guess >> (i*4)) & 0xf;
|
|
||||||
int a = (answer >> (i*4)) & 0xf;
|
|
||||||
|
|
||||||
if ((g < 1) || (g > 7)) {
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
} else if (g == a) {
|
|
||||||
ret += 0x10;
|
|
||||||
} else if (g & a) {
|
|
||||||
ret += 0x01;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%02x\n", ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue