mirror of
https://github.com/fooflington/wordsearch.git
synced 2025-04-22 16:29:08 +00:00
Compare commits
No commits in common. "master" and "20170417-1" have entirely different histories.
master
...
20170417-1
2
.gitignore
vendored
Executable file → Normal file
2
.gitignore
vendored
Executable file → Normal file
@ -1,5 +1,3 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
._.DS_Store
|
._.DS_Store
|
||||||
*.class
|
*.class
|
||||||
wordsearch.war
|
|
||||||
wordsearch.jar
|
|
||||||
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 Matthew Slowe
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
37
Makefile
Normal file → Executable file
37
Makefile
Normal file → Executable file
@ -1,34 +1,31 @@
|
|||||||
JAVA = /usr/bin/java
|
JAVA = /usr/bin/java
|
||||||
LIBS = .:war/WEB-INF/lib/commons-lang.jar:war/WEB-INF/lib/sqlite-jdbc.jar
|
# JAVAFLAGS = -version # -classpath $(LIBS)
|
||||||
JAVAC = /usr/bin/javac
|
JAVAC = /usr/bin/javac
|
||||||
JFLAGS = -g -classpath $(LIBS)
|
JFLAGS = -g # -classpath $(LIBS)
|
||||||
|
|
||||||
SRCS = uk/org/mafoo/wordsearch/CouldNotPlaceWordException.java \
|
SRCS = uk/org/mafoo/wordsearch/GridFactory.java \
|
||||||
uk/org/mafoo/wordsearch/Bounds.java \
|
uk/org/mafoo/wordsearch/Bounds.java \
|
||||||
uk/org/mafoo/wordsearch/GridFactory.java \
|
uk/org/mafoo/wordsearch/CouldNotPlaceWordException.java \
|
||||||
uk/org/mafoo/wordsearch/DistributedRandomNumberGenerator.java \
|
uk/org/mafoo/wordsearch/Direction.java \
|
||||||
uk/org/mafoo/wordsearch/Direction.java
|
uk/org/mafoo/wordsearch/DistributedRandomNumberGenerator.java
|
||||||
|
|
||||||
JSPS = $(wildcard war/*.jsp war/*.css war/WEB-INF/jspf/*.jspf)
|
|
||||||
|
|
||||||
OBJS = ${SRCS:.java=.class}
|
OBJS = ${SRCS:.java=.class}
|
||||||
|
|
||||||
.SUFFIXES: .java .class
|
.SUFFIXES: .java .class
|
||||||
|
|
||||||
.PHONY: default clean
|
all: build wordsearch.jar
|
||||||
default: wordsearch.war
|
|
||||||
|
|
||||||
clean:
|
run: all
|
||||||
rm -f $(OBJS) wordsearch.jar wordsearch.war
|
$(JAVA) uk.org.mafoo.wordsearch.GridFactory 30 30
|
||||||
|
|
||||||
.java.class:
|
.java.class:
|
||||||
$(JAVAC) $(JFLAGS) $<
|
$(JAVAC) $(JFLAGS) $<
|
||||||
|
|
||||||
wordsearch.jar: $(OBJS)
|
build: $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS) wordsearch.jar wordsearch.war
|
||||||
|
|
||||||
|
wordsearch.jar: build
|
||||||
jar cf $@ $(OBJS)
|
jar cf $@ $(OBJS)
|
||||||
|
cp wordsearch.jar war/WEB-INF/lib
|
||||||
war/WEB-INF/lib/wordsearch.jar: wordsearch.jar
|
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
wordsearch.war: war/WEB-INF/lib/wordsearch.jar $(JSPS)
|
|
||||||
jar -cvf $@ -C war .
|
|
||||||
|
11
README.md
Executable file → Normal file
11
README.md
Executable file → Normal file
@ -12,4 +12,15 @@ A simple ```Makefile``` is provided:
|
|||||||
$ make
|
$ make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Once built, you need to make a "war" file to deploy to your J2EE container (tested on Tomcat)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd war && jar cfv ../wordsearch.war .
|
||||||
|
```
|
||||||
Then deploy your war file :-)
|
Then deploy your war file :-)
|
||||||
|
|
||||||
|
Alternatively you can test-run the engine...
|
||||||
|
```
|
||||||
|
$ make run <wordlist.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
CREATE TABLE grids (id integer primary key, ts timestamp default current_timestamp, remotehost varchar, input, size_x int, size_y int, simple tinyint, result varchar);
|
|
12
src/Makefile
Normal file
12
src/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-DDEBUG_GRID_MAIN -std=c99 -g
|
||||||
|
DEPS=dir.o rnd.o grid.o
|
||||||
|
|
||||||
|
wordsearch: $(DEPS)
|
||||||
|
$(CC) -o wordsearch $(DEPS) $(CFLAGS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm $(DEPS)
|
39
src/dir.c
Normal file
39
src/dir.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "dir.h"
|
||||||
|
#include "rnd.h"
|
||||||
|
|
||||||
|
int directions[] = {
|
||||||
|
DIRECTION_N,
|
||||||
|
DIRECTION_NE,
|
||||||
|
DIRECTION_E,
|
||||||
|
DIRECTION_SE,
|
||||||
|
DIRECTION_S,
|
||||||
|
DIRECTION_SW,
|
||||||
|
DIRECTION_W,
|
||||||
|
DIRECTION_NW
|
||||||
|
};
|
||||||
|
|
||||||
|
int get_direction(int simple)
|
||||||
|
{
|
||||||
|
if (simple) {
|
||||||
|
if (random_number(0, 1) == 0) {
|
||||||
|
return DIRECTION_E;
|
||||||
|
} else {
|
||||||
|
return DIRECTION_S;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return directions[random_number(0, NUM_DIRECTIONS)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_DIR_MAIN
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int n = 32;
|
||||||
|
while (--n > 0) {
|
||||||
|
printf("%d\n", get_direction(1));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
19
src/dir.h
Normal file
19
src/dir.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef WORDSEARCH_DIR
|
||||||
|
#define WORDSEARCH_DIR
|
||||||
|
|
||||||
|
enum direction {
|
||||||
|
DIRECTION_N,
|
||||||
|
DIRECTION_NE,
|
||||||
|
DIRECTION_E,
|
||||||
|
DIRECTION_SE,
|
||||||
|
DIRECTION_S,
|
||||||
|
DIRECTION_SW,
|
||||||
|
DIRECTION_W,
|
||||||
|
DIRECTION_NW
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_DIRECTIONS 8
|
||||||
|
|
||||||
|
int get_direction(int simple);
|
||||||
|
|
||||||
|
#endif
|
197
src/grid.c
Executable file
197
src/grid.c
Executable file
@ -0,0 +1,197 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "rnd.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "grid.h"
|
||||||
|
|
||||||
|
enum exitcodes {
|
||||||
|
EXIT_WORDTOOLONG,
|
||||||
|
};
|
||||||
|
|
||||||
|
bounds *get_bounds(int height, int width, enum direction direction, int length)
|
||||||
|
{
|
||||||
|
if (length > height || length > width) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
length--;
|
||||||
|
|
||||||
|
bounds *b = (bounds *) malloc(sizeof(bounds));
|
||||||
|
|
||||||
|
b->min_x = 0;
|
||||||
|
b->max_x = width-1;
|
||||||
|
b->min_y = 0;
|
||||||
|
b->max_y = height-1;
|
||||||
|
if (direction == DIRECTION_N ||
|
||||||
|
direction == DIRECTION_NE || direction == DIRECTION_NW) {
|
||||||
|
b->min_y = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == DIRECTION_W ||
|
||||||
|
direction == DIRECTION_NW || direction == DIRECTION_SW) {
|
||||||
|
b->min_x = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == DIRECTION_E ||
|
||||||
|
direction == DIRECTION_NE || direction == DIRECTION_SE) {
|
||||||
|
b->max_x = width - length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == DIRECTION_S ||
|
||||||
|
direction == DIRECTION_SW || direction == DIRECTION_SE) {
|
||||||
|
b->max_y = height - length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int move_x(int x, enum direction d) {
|
||||||
|
if(
|
||||||
|
d == DIRECTION_E ||
|
||||||
|
d == DIRECTION_NE ||
|
||||||
|
d == DIRECTION_SE
|
||||||
|
) {
|
||||||
|
x++;
|
||||||
|
} else if (
|
||||||
|
d == DIRECTION_W ||
|
||||||
|
d == DIRECTION_NW ||
|
||||||
|
d == DIRECTION_SW
|
||||||
|
) {
|
||||||
|
x--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int move_y(int y, enum direction d) {
|
||||||
|
if(
|
||||||
|
d == DIRECTION_N ||
|
||||||
|
d == DIRECTION_NE ||
|
||||||
|
d == DIRECTION_NW
|
||||||
|
) {
|
||||||
|
y--;
|
||||||
|
} else if (
|
||||||
|
d == DIRECTION_S ||
|
||||||
|
d == DIRECTION_SE ||
|
||||||
|
d == DIRECTION_SW
|
||||||
|
) {
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **make_grid(char **words, int height, int width, int simple, int count)
|
||||||
|
{
|
||||||
|
char **grid = init_grid(NULL, height, width);
|
||||||
|
for(int i=0; i<count; i++) {
|
||||||
|
int tries = 0;
|
||||||
|
int placed = 0;
|
||||||
|
while(tries++ < WORDSEARCH_MAXTRIES && placed == 0) {
|
||||||
|
if(place_word(words[i], grid, height, width, simple)) {
|
||||||
|
placed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int place_word(char *word, char **grid, int height, int width, int simple)
|
||||||
|
{
|
||||||
|
int dir = get_direction(simple);
|
||||||
|
bounds *b;
|
||||||
|
|
||||||
|
if (b = get_bounds(height, width, dir, strlen(word))) {
|
||||||
|
/* word will probably fit... */
|
||||||
|
int x, y;
|
||||||
|
x = random_number(b->min_x, b->max_x);
|
||||||
|
y = random_number(b->min_y, b->max_y);
|
||||||
|
|
||||||
|
char** tempgrid = init_grid(grid, height, width);
|
||||||
|
/* Now we have two copies of the grid, try to place the word... */
|
||||||
|
int i;
|
||||||
|
for(i=0; i<strlen(word); i++) {
|
||||||
|
if(!isalpha(word[i])) continue;
|
||||||
|
if(tempgrid[y][x] != '_') {
|
||||||
|
if(tempgrid[y][x] != word[i]) {
|
||||||
|
/* Failed to place word */
|
||||||
|
free_grid(tempgrid, height);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* word crossed ok! */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempgrid[y][x] = word[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
x = move_x(x, dir);
|
||||||
|
y = move_y(y, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_grid(grid, height);
|
||||||
|
free(b);
|
||||||
|
grid = tempgrid;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
/* word can't fit */
|
||||||
|
printf("[ERR] Word too long to place in this grid: %s\n", word);
|
||||||
|
exit(EXIT_WORDTOOLONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clones or creates an empty grid - pass NULL in as old to get an empty grid */
|
||||||
|
char **init_grid(char** old, int height, int width)
|
||||||
|
{
|
||||||
|
int row, cell;
|
||||||
|
char **new = malloc(height * sizeof(char*));
|
||||||
|
|
||||||
|
for (row=0; row < height; row++) {
|
||||||
|
new[row] = malloc(width * sizeof(char));
|
||||||
|
for (cell=0; cell < width; cell++) {
|
||||||
|
if(old) {
|
||||||
|
new[row][cell] = old[row][cell];
|
||||||
|
} else {
|
||||||
|
new[row][cell] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_grid(char** grid, int height) {
|
||||||
|
for (int i=0; i<height; i++) {
|
||||||
|
free(grid[i]);
|
||||||
|
}
|
||||||
|
free(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_grid(char** grid, int height) {
|
||||||
|
for(int i=0; i<height; i++) {
|
||||||
|
if(grid[i] == NULL) {
|
||||||
|
printf("row error\n");
|
||||||
|
} else {
|
||||||
|
printf("%s\n", grid[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_GRID_MAIN
|
||||||
|
#include <stdio.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char *words[4] = {
|
||||||
|
"test",
|
||||||
|
"word",
|
||||||
|
"longer",
|
||||||
|
"verylong"
|
||||||
|
};
|
||||||
|
char **grid = make_grid(words, 10, 10, 0, 4);
|
||||||
|
print_grid(grid, 4);
|
||||||
|
}
|
||||||
|
#endif
|
26
src/grid.h
Normal file
26
src/grid.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef WORDSEARCH_GRID
|
||||||
|
#define WORDSEARCH_GRID
|
||||||
|
|
||||||
|
#define WORDSEARCH_MAXTRIES 500
|
||||||
|
|
||||||
|
char **make_grid(char **words, int height, int width, int simple, int count);
|
||||||
|
|
||||||
|
typedef struct bounds {
|
||||||
|
int min_y;
|
||||||
|
int max_y;
|
||||||
|
int min_x;
|
||||||
|
int max_x;
|
||||||
|
} bounds;
|
||||||
|
|
||||||
|
/* returns NULL if cannot fit word; caller needs to free() the response */
|
||||||
|
bounds *get_bounds(int height, int width, enum direction direction, int length);
|
||||||
|
|
||||||
|
int place_word(char *word, char **grid, int height, int width, int simple);
|
||||||
|
|
||||||
|
char **init_grid(char** old, int height, int width);
|
||||||
|
void free_grid(char** grid, int height);
|
||||||
|
int move_x(int x, enum direction d);
|
||||||
|
int move_y(int y, enum direction d);
|
||||||
|
void print_grid(char** grid, int height);
|
||||||
|
|
||||||
|
#endif
|
90
src/rnd.c
Normal file
90
src/rnd.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "rnd.h"
|
||||||
|
|
||||||
|
/* https://www.math.cornell.edu/~mec/2003-2004/cryptography/subs/frequencies.html */
|
||||||
|
|
||||||
|
#define ALPHABET_SIZE 26
|
||||||
|
#define RND_MAXINT 10000
|
||||||
|
|
||||||
|
struct letter_frequency {
|
||||||
|
int p; /* pegged to 10000 rather than 1 */
|
||||||
|
char c;
|
||||||
|
} letter_frequencies[] = {
|
||||||
|
{
|
||||||
|
7, 'Z'}, {
|
||||||
|
10, 'J'}, {
|
||||||
|
11, 'Q'}, {
|
||||||
|
17, 'X'}, {
|
||||||
|
69, 'K'}, {
|
||||||
|
111, 'V'}, {
|
||||||
|
149, 'B'}, {
|
||||||
|
182, 'P'}, {
|
||||||
|
203, 'G'}, {
|
||||||
|
209, 'W'}, {
|
||||||
|
211, 'Y'}, {
|
||||||
|
230, 'F'}, {
|
||||||
|
261, 'M'}, {
|
||||||
|
271, 'C'}, {
|
||||||
|
288, 'U'}, {
|
||||||
|
398, 'L'}, {
|
||||||
|
432, 'D'}, {
|
||||||
|
592, 'H'}, {
|
||||||
|
602, 'R'}, {
|
||||||
|
628, 'S'}, {
|
||||||
|
695, 'N'}, {
|
||||||
|
731, 'I'}, {
|
||||||
|
768, 'O'}, {
|
||||||
|
812, 'A'}, {
|
||||||
|
910, 'T'}, {
|
||||||
|
1202, 'E'},};
|
||||||
|
|
||||||
|
/* from http://stackoverflow.com/questions/822323/how-to-generate-a-random-number-in-c */
|
||||||
|
int random_number(int min_num, int max_num)
|
||||||
|
{
|
||||||
|
static int initialised = 0;
|
||||||
|
int result = 0;
|
||||||
|
int low_num = 0;
|
||||||
|
int hi_num = 0;
|
||||||
|
if (min_num < max_num) {
|
||||||
|
low_num = min_num;
|
||||||
|
hi_num = max_num + 1; // this is done to include max_num in output.
|
||||||
|
} else {
|
||||||
|
low_num = max_num + 1; // this is done to include max_num in output.
|
||||||
|
hi_num = min_num;
|
||||||
|
}
|
||||||
|
if (!initialised) {
|
||||||
|
srand(time(NULL));
|
||||||
|
initialised = 1;
|
||||||
|
}
|
||||||
|
result = (rand() % (hi_num - low_num)) + low_num;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char get_random_letter()
|
||||||
|
{
|
||||||
|
int rnd = random_number(0, RND_MAXINT);
|
||||||
|
for (int i = 0; i < ALPHABET_SIZE; i++) {
|
||||||
|
if (rnd < letter_frequencies[i].p) {
|
||||||
|
return letter_frequencies[i].c;
|
||||||
|
}
|
||||||
|
rnd -= letter_frequencies[i].p;
|
||||||
|
}
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_RND_MAIN
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int n = 32;
|
||||||
|
while (--n > 0) {
|
||||||
|
printf("%c", get_random_letter());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
7
src/rnd.h
Normal file
7
src/rnd.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef WORDSEARCH_RND
|
||||||
|
#define WORDSEARCH_RND
|
||||||
|
|
||||||
|
char get_random_letter();
|
||||||
|
int random_number(int min_num, int max_num);
|
||||||
|
|
||||||
|
#endif
|
9
test.txt
9
test.txt
@ -1,9 +0,0 @@
|
|||||||
Kitchen
|
|
||||||
Lounge
|
|
||||||
Study
|
|
||||||
Ballroom
|
|
||||||
Conservatory
|
|
||||||
Billiard Room
|
|
||||||
Library
|
|
||||||
Hall
|
|
||||||
Dining Room
|
|
0
uk/org/mafoo/wordsearch/Direction.java
Executable file → Normal file
0
uk/org/mafoo/wordsearch/Direction.java
Executable file → Normal file
@ -6,7 +6,7 @@ import java.util.*;
|
|||||||
public class GridFactory {
|
public class GridFactory {
|
||||||
|
|
||||||
static Random rnd = new Random();
|
static Random rnd = new Random();
|
||||||
static final int MAX_TRIES = 5000;
|
static final int MAX_TRIES = 500;
|
||||||
|
|
||||||
/* https://www.math.cornell.edu/~mec/2003-2004/cryptography/subs/frequencies.html */
|
/* https://www.math.cornell.edu/~mec/2003-2004/cryptography/subs/frequencies.html */
|
||||||
static DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
|
static DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
|
||||||
@ -158,10 +158,6 @@ public class GridFactory {
|
|||||||
return makeGrid(words, height, width, false, true);
|
return makeGrid(words, height, width, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char[][] makeGrid(List<String> words, int height, int width, boolean simple) {
|
|
||||||
return makeGrid(words, height, width, simple, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static char[][] makeGrid(List<String> words, int height, int width, boolean simple, boolean fill) {
|
public static char[][] makeGrid(List<String> words, int height, int width, boolean simple, boolean fill) {
|
||||||
char[][] grid = new char[height][width];
|
char[][] grid = new char[height][width];
|
||||||
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<div id="footer">
|
|
||||||
Generated at: https://bombadil.mafoo.org.uk/wordsearch/
|
|
||||||
</div>
|
|
0
war/WEB-INF/lib/commons-lang.jar
Executable file → Normal file
0
war/WEB-INF/lib/commons-lang.jar
Executable file → Normal file
0
war/WEB-INF/lib/json-simple-1.1.1.jar
Executable file → Normal file
0
war/WEB-INF/lib/json-simple-1.1.1.jar
Executable file → Normal file
@ -10,20 +10,4 @@
|
|||||||
Wordsearch building app
|
Wordsearch building app
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<context-param>
|
|
||||||
<param-name>sqlite_db</param-name>
|
|
||||||
<param-value>/tmp/wordsearch.db</param-value>
|
|
||||||
</context-param>
|
|
||||||
|
|
||||||
<security-constraint>
|
|
||||||
<web-resource-collection>
|
|
||||||
<web-resource-name>dump</web-resource-name>
|
|
||||||
<url-pattern>/dump.jsp</url-pattern>
|
|
||||||
</web-resource-collection>
|
|
||||||
<auth-constraint>
|
|
||||||
<role-name>admin</role-name>
|
|
||||||
</auth-constraint>
|
|
||||||
</security-constraint>
|
|
||||||
|
|
||||||
|
|
||||||
</web-app>
|
</web-app>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Wordsearch Builder: About</title>
|
<title>Wordsearch Builder: About</title>
|
||||||
<link rel="stylesheet" type="text/css" href="base.css" />
|
<link rel="stylesheet" type="text/css" href="base.css" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500&family=Roboto:wght@500&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
0
war/api.jsp
Executable file → Normal file
0
war/api.jsp
Executable file → Normal file
18
war/base.css
Executable file → Normal file
18
war/base.css
Executable file → Normal file
@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
@ -14,16 +14,17 @@ table, th, td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table#grid td {
|
table#grid td {
|
||||||
font-family: 'Fira Mono', monospace;
|
font-family: monospace;
|
||||||
font-size: 14pt;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
align-content: center;
|
width: 80%;
|
||||||
|
margin: 0 auto 60px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wordsearch {
|
#wordsearch {
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#words {
|
#words {
|
||||||
@ -34,11 +35,8 @@ table#grid td {
|
|||||||
#footer {
|
#footer {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
bottom:0;
|
bottom:0;
|
||||||
}
|
width:100%;
|
||||||
|
height:60px; /* Height of the footer */
|
||||||
textarea#textarea_words {
|
|
||||||
font-family: 'Fira Mono', monospace;
|
|
||||||
font-size: 10pt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only print {
|
@media only print {
|
||||||
|
@ -3,15 +3,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Wordsearch builder</title>
|
<title>Wordsearch builder</title>
|
||||||
<link rel="stylesheet" type="text/css" href="base.css" />
|
<link rel="stylesheet" type="text/css" href="base.css" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500&family=Roboto:wght@500&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<%@ page import="uk.org.mafoo.wordsearch.*" %>
|
<%@ page import="uk.org.mafoo.wordsearch.*" %>
|
||||||
<%@ page import="java.util.*" %>
|
<%@ page import="java.util.*" %>
|
||||||
<%@ page import="org.apache.commons.lang.StringUtils" %>
|
<%@ page import="org.apache.commons.lang.StringUtils" %>
|
||||||
<%@ page import="org.apache.commons.lang.StringEscapeUtils" %>
|
<%@ page import="org.apache.commons.lang.StringEscapeUtils" %>
|
||||||
<%@ page import="java.sql.*" %>
|
|
||||||
<%@ page import="org.sqlite.*" %>
|
|
||||||
|
|
||||||
<%@ page errorPage="error.jsp" %>
|
<%@ page errorPage="error.jsp" %>
|
||||||
<%
|
<%
|
||||||
|
|
||||||
@ -25,55 +21,24 @@
|
|||||||
|
|
||||||
List<String> words = new ArrayList<String>();
|
List<String> words = new ArrayList<String>();
|
||||||
for ( String line : request.getParameter("words").split("\r\n")) {
|
for ( String line : request.getParameter("words").split("\r\n")) {
|
||||||
words.add(line.trim().toLowerCase());
|
words.add(line.trim());
|
||||||
}
|
}
|
||||||
Collections.sort(words);
|
Collections.sort(words);
|
||||||
|
|
||||||
Connection conn = DriverManager.getConnection(
|
char[][] grid = GridFactory.makeGrid(words, height, width, simple);
|
||||||
"jdbc:sqlite:" + getServletContext().getRealPath("/WEB-INF/files/database.sqlite"));
|
|
||||||
PreparedStatement stmt = conn.prepareStatement(
|
|
||||||
"INSERT INTO grids (remotehost, input, size_x, size_y, simple) VALUES (?, ?, ?, ?, ?)",
|
|
||||||
Statement.RETURN_GENERATED_KEYS);
|
|
||||||
// PreparedStatement getlastid = conn.prepareStatement("select last_insert_rowid();");
|
|
||||||
PreparedStatement stmt2 = conn.prepareStatement("UPDATE grids SET result=? WHERE id=?");
|
|
||||||
|
|
||||||
stmt.setString(1, request.getRemoteHost());
|
String csv = "";
|
||||||
stmt.setString(2, words.toString());
|
|
||||||
stmt.setInt(3, height);
|
|
||||||
stmt.setInt(4, width);
|
|
||||||
stmt.setBoolean(5, simple);
|
|
||||||
stmt.executeUpdate();
|
|
||||||
ResultSet record_id_rs = stmt.getGeneratedKeys();
|
|
||||||
int record_id = -1;
|
|
||||||
if(record_id_rs.next()){
|
|
||||||
record_id = record_id_rs.getInt(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually generate the grid
|
|
||||||
char[][] grid = GridFactory.makeGrid(words, height, width, simple);
|
|
||||||
|
|
||||||
StringBuilder _csv = new StringBuilder();
|
|
||||||
for (char[] cs : grid) {
|
|
||||||
for(char c : cs) {
|
|
||||||
_csv.append(c);
|
|
||||||
_csv.append(",");
|
|
||||||
}
|
|
||||||
_csv.append("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
String csv = _csv.toString();
|
|
||||||
|
|
||||||
stmt2.setString(1, csv);
|
|
||||||
stmt2.setInt(2, record_id);
|
|
||||||
stmt2.executeUpdate();
|
|
||||||
conn.close();
|
|
||||||
%>
|
%>
|
||||||
<body>
|
<body>
|
||||||
<h1><%= name %></h1>
|
<h1><%= name %></h1>
|
||||||
<div class="noprint">
|
<div class="noprint">
|
||||||
[ <a href="index.jsp">Start again</a> | <a id='csvdownload' href="data:text/csv;base64,<%= Base64.getEncoder().encodeToString(csv.getBytes()) %>">Download CSV</a> ]
|
[ <a href="index.jsp">Start again</a> | <a id='csvdownload'>Download CSV</a> ]
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
var csv = '<%= csv %>';
|
||||||
|
var csvdownload = document.getElementById('csvdownload');
|
||||||
|
csvdownload.href='data:text/csv;base64,' + btoa(csv);
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
|
53
war/db.jsp
53
war/db.jsp
@ -1,53 +0,0 @@
|
|||||||
<%@ page contentType="text/html" %>
|
|
||||||
<%@ page import="java.sql.*" %>
|
|
||||||
<%@ page import="org.sqlite.*" %>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Database dump</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Database stats</h1>
|
|
||||||
<%
|
|
||||||
Connection conn =
|
|
||||||
DriverManager.getConnection("jdbc:sqlite:" + getServletContext().getRealPath("/WEB-INF/files/database.sqlite"));
|
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
ResultSet rs = stmt.executeQuery("select * from sqlite_master where type='table' and name='grids';");
|
|
||||||
if(rs.next()) {
|
|
||||||
// we have a row and are probably ok
|
|
||||||
} else {
|
|
||||||
// Initialise the schema
|
|
||||||
PreparedStatement pstmt = conn.prepareStatement("CREATE TABLE grids (id integer primary key, ts timestamp default current_timestamp, remotehost varchar, input, size_x int, size_y int, simple tinyint, result varchar);");
|
|
||||||
pstmt.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
rs.close();
|
|
||||||
|
|
||||||
PreparedStatement ps_count = conn.prepareStatement("SELECT COUNT(*) FROM grids");
|
|
||||||
PreparedStatement ps_last = conn.prepareStatement("SELECT max(ts) FROM grids;");
|
|
||||||
|
|
||||||
ResultSet rs_count = ps_count.executeQuery();
|
|
||||||
ResultSet rs_last = ps_last.executeQuery();
|
|
||||||
int count = -1;
|
|
||||||
String last = "unknown";
|
|
||||||
if(rs_count.next()) {
|
|
||||||
count = rs_count.getInt(1);
|
|
||||||
}
|
|
||||||
if(rs_last.next()) {
|
|
||||||
last = rs_last.getString(1);
|
|
||||||
}
|
|
||||||
rs_count.close();
|
|
||||||
rs_last.close();
|
|
||||||
%>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Number of grids generated: <%= count %></li>
|
|
||||||
<li>Last grid generated at: <%= last %></li>
|
|
||||||
<li>Path to db: <pre><%= getServletContext().getRealPath("/WEB-INF/files/database.sqlite") %></pre></li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
<%
|
|
||||||
conn.close();
|
|
||||||
%>
|
|
||||||
</html>
|
|
50
war/dump.jsp
50
war/dump.jsp
@ -1,50 +0,0 @@
|
|||||||
<%@ page contentType="text/html" %>
|
|
||||||
<%@ page import="java.sql.*" %>
|
|
||||||
<%@ page import="org.sqlite.*" %>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Database dump</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table border="1">
|
|
||||||
<thead>
|
|
||||||
<th>id</th>
|
|
||||||
<th>timestamp</th>
|
|
||||||
<th>remotehost</th>
|
|
||||||
<th>input</th>
|
|
||||||
<th>size_x</th>
|
|
||||||
<th>size_y</th>
|
|
||||||
<th>simple</th>
|
|
||||||
<th>result</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<%
|
|
||||||
Connection conn =
|
|
||||||
DriverManager.getConnection("jdbc:sqlite:" + getServletContext().getInitParameter("sqlite_db"));
|
|
||||||
Statement stat = conn.createStatement();
|
|
||||||
|
|
||||||
ResultSet rs = stat.executeQuery("select * from grids;");
|
|
||||||
|
|
||||||
while (rs.next()) {
|
|
||||||
out.println("<tr>");
|
|
||||||
out.println("<td>" + rs.getString("id") + "</td>");
|
|
||||||
out.println("<td>" + rs.getString("ts") + "</td>");
|
|
||||||
out.println("<td>" + rs.getString("remotehost") + "</td>");
|
|
||||||
out.println("<td><pre>" + rs.getString("input") + "</pre></td>");
|
|
||||||
out.println("<td>" + rs.getInt("size_x") + "</td>");
|
|
||||||
out.println("<td>" + rs.getInt("size_y") + "</td>");
|
|
||||||
out.println("<td>" + rs.getInt("simple") + "</td>");
|
|
||||||
out.println("<td><pre>" + rs.getString("result") + "</pre></td>");
|
|
||||||
out.println("</tr>");
|
|
||||||
}
|
|
||||||
|
|
||||||
rs.close();
|
|
||||||
conn.close();
|
|
||||||
%>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
22
war/env.jsp
22
war/env.jsp
@ -1,22 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>
|
|
||||||
<h1>Environment</h1>
|
|
||||||
<ul>
|
|
||||||
<li>You are connecting from: <%= request.getRemoteHost() %> (<%= request.getRemoteAddr() %>)</li>
|
|
||||||
<li>If present your X-Forwarded-For header is: <%= request.getHeader("X-Forwarded-For") %></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Header dump</h2>
|
|
||||||
<pre>
|
|
||||||
<%
|
|
||||||
for (java.util.Enumeration<String> headers = request.getHeaderNames(); headers.hasMoreElements(); ) {
|
|
||||||
String h = headers.nextElement();
|
|
||||||
%>
|
|
||||||
<%= h %>=<%= request.getHeader(h) %>
|
|
||||||
<%
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
@ -3,7 +3,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Wordsearch Builder</title>
|
<title>Wordsearch Builder</title>
|
||||||
<link rel="stylesheet" type="text/css" href="base.css" />
|
<link rel="stylesheet" type="text/css" href="base.css" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500&family=Roboto:wght@500&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Wordsearch Builder</h1>
|
<h1>Wordsearch Builder</h1>
|
||||||
@ -12,7 +11,7 @@
|
|||||||
<h2>Words</h2>
|
<h2>Words</h2>
|
||||||
<form action="build.jsp" method="post">
|
<form action="build.jsp" method="post">
|
||||||
Name: <input type="text" size="25" name="name" value="Wordsearch"><br />
|
Name: <input type="text" size="25" name="name" value="Wordsearch"><br />
|
||||||
<textarea id="textarea_words" name="words" rows="10">
|
<textarea name="words" rows="10">
|
||||||
Kitchen
|
Kitchen
|
||||||
Lounge
|
Lounge
|
||||||
Study
|
Study
|
||||||
|
Loading…
x
Reference in New Issue
Block a user