mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
New puzzle: `Tents'. Requires a potentially shared algorithms module
maxflow.c. Also in this checkin, fixes to the OS X and GTK back ends to get ALIGN_VNORMAL right. This is the first time I've used it! :-) [originally from svn r6390]
This commit is contained in:
9
Recipe
9
Recipe
@ -26,10 +26,11 @@ SLANT = slant dsf
|
|||||||
MAP = map dsf
|
MAP = map dsf
|
||||||
LOOPY = loopy tree234 dsf
|
LOOPY = loopy tree234 dsf
|
||||||
LIGHTUP = lightup combi
|
LIGHTUP = lightup combi
|
||||||
|
TENTS = tents maxflow
|
||||||
|
|
||||||
ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle
|
ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle
|
||||||
+ MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox SLANT
|
+ MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox SLANT
|
||||||
+ LIGHTUP MAP LOOPY inertia
|
+ LIGHTUP MAP LOOPY inertia TENTS
|
||||||
|
|
||||||
GTK = gtk printing ps
|
GTK = gtk printing ps
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ lightup : [X] GTK COMMON LIGHTUP
|
|||||||
map : [X] GTK COMMON MAP
|
map : [X] GTK COMMON MAP
|
||||||
loopy : [X] GTK COMMON LOOPY
|
loopy : [X] GTK COMMON LOOPY
|
||||||
inertia : [X] GTK COMMON inertia
|
inertia : [X] GTK COMMON inertia
|
||||||
|
tents : [X] GTK COMMON TENTS
|
||||||
|
|
||||||
# Auxiliary command-line programs.
|
# Auxiliary command-line programs.
|
||||||
STANDALONE = nullfe random misc malloc
|
STANDALONE = nullfe random misc malloc
|
||||||
@ -65,6 +67,7 @@ mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
|
|||||||
slantsolver : [U] slant[STANDALONE_SOLVER] dsf STANDALONE
|
slantsolver : [U] slant[STANDALONE_SOLVER] dsf STANDALONE
|
||||||
mapsolver : [U] map[STANDALONE_SOLVER] dsf STANDALONE m.lib
|
mapsolver : [U] map[STANDALONE_SOLVER] dsf STANDALONE m.lib
|
||||||
lightupsolver : [U] lightup[STANDALONE_SOLVER] combi STANDALONE
|
lightupsolver : [U] lightup[STANDALONE_SOLVER] combi STANDALONE
|
||||||
|
tentssolver : [U] tents[STANDALONE_SOLVER] maxflow STANDALONE
|
||||||
|
|
||||||
solosolver : [C] solo[STANDALONE_SOLVER] STANDALONE
|
solosolver : [C] solo[STANDALONE_SOLVER] STANDALONE
|
||||||
patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
|
patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
|
||||||
@ -72,6 +75,7 @@ mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
|
|||||||
slantsolver : [C] slant[STANDALONE_SOLVER] dsf STANDALONE
|
slantsolver : [C] slant[STANDALONE_SOLVER] dsf STANDALONE
|
||||||
mapsolver : [C] map[STANDALONE_SOLVER] dsf STANDALONE
|
mapsolver : [C] map[STANDALONE_SOLVER] dsf STANDALONE
|
||||||
lightupsolver : [C] lightup[STANDALONE_SOLVER] combi STANDALONE
|
lightupsolver : [C] lightup[STANDALONE_SOLVER] combi STANDALONE
|
||||||
|
tentssolver : [C] tents[STANDALONE_SOLVER] maxflow STANDALONE
|
||||||
|
|
||||||
# The Windows Net shouldn't be called `net.exe' since Windows
|
# The Windows Net shouldn't be called `net.exe' since Windows
|
||||||
# already has a reasonably important utility program by that name!
|
# already has a reasonably important utility program by that name!
|
||||||
@ -97,6 +101,7 @@ lightup : [G] WINDOWS COMMON LIGHTUP
|
|||||||
map : [G] WINDOWS COMMON MAP
|
map : [G] WINDOWS COMMON MAP
|
||||||
loopy : [G] WINDOWS COMMON LOOPY
|
loopy : [G] WINDOWS COMMON LOOPY
|
||||||
inertia : [G] WINDOWS COMMON inertia
|
inertia : [G] WINDOWS COMMON inertia
|
||||||
|
tents : [G] WINDOWS COMMON TENTS
|
||||||
|
|
||||||
# Mac OS X unified application containing all the puzzles.
|
# Mac OS X unified application containing all the puzzles.
|
||||||
Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
|
Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
|
||||||
@ -189,7 +194,7 @@ install:
|
|||||||
for i in cube net netslide fifteen sixteen twiddle \
|
for i in cube net netslide fifteen sixteen twiddle \
|
||||||
pattern rect solo mines samegame flip guess \
|
pattern rect solo mines samegame flip guess \
|
||||||
pegs dominosa untangle blackbox slant lightup \
|
pegs dominosa untangle blackbox slant lightup \
|
||||||
map loopy inertia; do \
|
map loopy inertia tents; do \
|
||||||
$(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i \
|
$(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i \
|
||||||
|| exit 1; \
|
|| exit 1; \
|
||||||
done
|
done
|
||||||
|
4
gtk.c
4
gtk.c
@ -290,6 +290,8 @@ void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
|
|||||||
|
|
||||||
if (align & ALIGN_VCENTRE)
|
if (align & ALIGN_VCENTRE)
|
||||||
rect.y -= rect.height / 2;
|
rect.y -= rect.height / 2;
|
||||||
|
else
|
||||||
|
rect.y -= rect.height;
|
||||||
|
|
||||||
if (align & ALIGN_HCENTRE)
|
if (align & ALIGN_HCENTRE)
|
||||||
rect.x -= rect.width / 2;
|
rect.x -= rect.width / 2;
|
||||||
@ -317,6 +319,8 @@ void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
|
|||||||
&lb, &rb, &wid, &asc, &desc);
|
&lb, &rb, &wid, &asc, &desc);
|
||||||
if (align & ALIGN_VCENTRE)
|
if (align & ALIGN_VCENTRE)
|
||||||
y += asc - (asc+desc)/2;
|
y += asc - (asc+desc)/2;
|
||||||
|
else
|
||||||
|
y += asc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ... but horizontal extents with respect to the provided
|
* ... but horizontal extents with respect to the provided
|
||||||
|
2
list.c
2
list.c
@ -37,6 +37,7 @@ extern const game samegame;
|
|||||||
extern const game sixteen;
|
extern const game sixteen;
|
||||||
extern const game slant;
|
extern const game slant;
|
||||||
extern const game solo;
|
extern const game solo;
|
||||||
|
extern const game tents;
|
||||||
extern const game twiddle;
|
extern const game twiddle;
|
||||||
extern const game untangle;
|
extern const game untangle;
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ const game *gamelist[] = {
|
|||||||
&sixteen,
|
&sixteen,
|
||||||
&slant,
|
&slant,
|
||||||
&solo,
|
&solo,
|
||||||
|
&tents,
|
||||||
&twiddle,
|
&twiddle,
|
||||||
&untangle,
|
&untangle,
|
||||||
};
|
};
|
||||||
|
461
maxflow.c
Normal file
461
maxflow.c
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
/*
|
||||||
|
* Edmonds-Karp algorithm for finding a maximum flow and minimum
|
||||||
|
* cut in a network. Almost identical to the Ford-Fulkerson
|
||||||
|
* algorithm, but apparently using breadth-first search to find the
|
||||||
|
* _shortest_ augmenting path is a good way to guarantee
|
||||||
|
* termination and ensure the time complexity is not dependent on
|
||||||
|
* the actual value of the maximum flow. I don't understand why
|
||||||
|
* that should be, but it's claimed on the Internet that it's been
|
||||||
|
* proved, and that's good enough for me. I prefer BFS to DFS
|
||||||
|
* anyway :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "maxflow.h"
|
||||||
|
|
||||||
|
#include "puzzles.h" /* for snewn/sfree */
|
||||||
|
|
||||||
|
int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
|
||||||
|
int ne, const int *edges, const int *backedges,
|
||||||
|
const int *capacity, int *flow, int *cut)
|
||||||
|
{
|
||||||
|
int *todo = (int *)scratch;
|
||||||
|
int *prev = todo + nv;
|
||||||
|
int *firstedge = todo + 2*nv;
|
||||||
|
int *firstbackedge = todo + 3*nv;
|
||||||
|
int i, j, head, tail, from, to;
|
||||||
|
int totalflow;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the edges array to find the index of the first edge
|
||||||
|
* from each node.
|
||||||
|
*/
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < ne; i++)
|
||||||
|
while (j <= edges[2*i])
|
||||||
|
firstedge[j++] = i;
|
||||||
|
while (j < nv)
|
||||||
|
firstedge[j++] = ne;
|
||||||
|
assert(j == nv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the backedges array to find the index of the first edge
|
||||||
|
* _to_ each node.
|
||||||
|
*/
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < ne; i++)
|
||||||
|
while (j <= edges[2*backedges[i]+1])
|
||||||
|
firstbackedge[j++] = i;
|
||||||
|
while (j < nv)
|
||||||
|
firstbackedge[j++] = ne;
|
||||||
|
assert(j == nv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the flow off at zero on every edge.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ne; i++)
|
||||||
|
flow[i] = 0;
|
||||||
|
totalflow = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeatedly look for an augmenting path, and follow it.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the prev array.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < nv; i++)
|
||||||
|
prev[i] = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise the to-do list for BFS.
|
||||||
|
*/
|
||||||
|
head = tail = 0;
|
||||||
|
todo[tail++] = source;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now do the BFS loop.
|
||||||
|
*/
|
||||||
|
while (head < tail && prev[sink] <= 0) {
|
||||||
|
from = todo[head++];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try all the forward edges out of node `from'. For a
|
||||||
|
* forward edge to be valid, it must have flow
|
||||||
|
* currently less than its capacity.
|
||||||
|
*/
|
||||||
|
for (i = firstedge[from]; i < ne && edges[2*i] == from; i++) {
|
||||||
|
to = edges[2*i+1];
|
||||||
|
if (to == source || prev[to] >= 0)
|
||||||
|
continue;
|
||||||
|
if (capacity[i] >= 0 && flow[i] >= capacity[i])
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* This is a valid augmenting edge. Visit node `to'.
|
||||||
|
*/
|
||||||
|
prev[to] = 2*i;
|
||||||
|
todo[tail++] = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try all the backward edges into node `from'. For a
|
||||||
|
* backward edge to be valid, it must have flow
|
||||||
|
* currently greater than zero.
|
||||||
|
*/
|
||||||
|
for (i = firstbackedge[from];
|
||||||
|
j = backedges[i], i < ne && edges[2*j+1]==from; i++) {
|
||||||
|
to = edges[2*j];
|
||||||
|
if (to == source || prev[to] >= 0)
|
||||||
|
continue;
|
||||||
|
if (flow[j] <= 0)
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* This is a valid augmenting edge. Visit node `to'.
|
||||||
|
*/
|
||||||
|
prev[to] = 2*j+1;
|
||||||
|
todo[tail++] = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If prev[sink] is non-null, we have found an augmenting
|
||||||
|
* path.
|
||||||
|
*/
|
||||||
|
if (prev[sink] >= 0) {
|
||||||
|
int max;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work backwards along the path figuring out the
|
||||||
|
* maximum flow we can add.
|
||||||
|
*/
|
||||||
|
to = sink;
|
||||||
|
max = -1;
|
||||||
|
while (to != source) {
|
||||||
|
int spare;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the edge we're currently moving along.
|
||||||
|
*/
|
||||||
|
i = prev[to];
|
||||||
|
from = edges[i];
|
||||||
|
assert(from != to);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the spare capacity of this edge.
|
||||||
|
*/
|
||||||
|
if (i & 1)
|
||||||
|
spare = flow[i / 2]; /* backward edge */
|
||||||
|
else if (capacity[i / 2] >= 0)
|
||||||
|
spare = capacity[i / 2] - flow[i / 2]; /* forward edge */
|
||||||
|
else
|
||||||
|
spare = -1; /* unlimited forward edge */
|
||||||
|
|
||||||
|
assert(spare != 0);
|
||||||
|
|
||||||
|
if (max < 0 || (spare >= 0 && spare < max))
|
||||||
|
max = spare;
|
||||||
|
|
||||||
|
to = from;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Fail an assertion if max is still < 0, i.e. there is
|
||||||
|
* an entirely unlimited path from source to sink. Also
|
||||||
|
* max should not _be_ zero, because by construction
|
||||||
|
* this _should_ be an augmenting path.
|
||||||
|
*/
|
||||||
|
assert(max > 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now work backwards along the path again, this time
|
||||||
|
* actually adjusting the flow.
|
||||||
|
*/
|
||||||
|
to = sink;
|
||||||
|
while (to != source) {
|
||||||
|
/*
|
||||||
|
* Find the edge we're currently moving along.
|
||||||
|
*/
|
||||||
|
i = prev[to];
|
||||||
|
from = edges[i];
|
||||||
|
assert(from != to);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the edge.
|
||||||
|
*/
|
||||||
|
if (i & 1)
|
||||||
|
flow[i / 2] -= max; /* backward edge */
|
||||||
|
else
|
||||||
|
flow[i / 2] += max; /* forward edge */
|
||||||
|
|
||||||
|
to = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And adjust the overall flow counter.
|
||||||
|
*/
|
||||||
|
totalflow += max;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we reach here, we have failed to find an augmenting
|
||||||
|
* path, which means we're done. Output the `cut' array if
|
||||||
|
* required, and leave.
|
||||||
|
*/
|
||||||
|
if (cut) {
|
||||||
|
for (i = 0; i < nv; i++) {
|
||||||
|
if (i == source || prev[i] >= 0)
|
||||||
|
cut[i] = 0;
|
||||||
|
else
|
||||||
|
cut[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxflow_scratch_size(int nv)
|
||||||
|
{
|
||||||
|
return (nv * 4) * sizeof(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
void maxflow_setup_backedges(int ne, const int *edges, int *backedges)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
for (i = 0; i < ne; i++)
|
||||||
|
backedges[i] = i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We actually can't use the C qsort() function, because we'd
|
||||||
|
* need to pass `edges' as a context parameter to its
|
||||||
|
* comparator function. So instead I'm forced to implement my
|
||||||
|
* own sorting algorithm internally, which is a pest. I'll use
|
||||||
|
* heapsort, because I like it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LESS(i,j) ( (edges[2*(i)+1] < edges[2*(j)+1]) || \
|
||||||
|
(edges[2*(i)+1] == edges[2*(j)+1] && \
|
||||||
|
edges[2*(i)] < edges[2*(j)]) )
|
||||||
|
#define PARENT(n) ( ((n)-1)/2 )
|
||||||
|
#define LCHILD(n) ( 2*(n)+1 )
|
||||||
|
#define RCHILD(n) ( 2*(n)+2 )
|
||||||
|
#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Phase 1: build the heap. We want the _largest_ element at
|
||||||
|
* the top.
|
||||||
|
*/
|
||||||
|
n = 0;
|
||||||
|
while (n < ne) {
|
||||||
|
n++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Swap element n with its parent repeatedly to preserve
|
||||||
|
* the heap property.
|
||||||
|
*/
|
||||||
|
i = n-1;
|
||||||
|
|
||||||
|
while (i > 0) {
|
||||||
|
int p = PARENT(i);
|
||||||
|
|
||||||
|
if (LESS(backedges[p], backedges[i])) {
|
||||||
|
SWAP(backedges[p], backedges[i]);
|
||||||
|
i = p;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Phase 2: repeatedly remove the largest element and stick it
|
||||||
|
* at the top of the array.
|
||||||
|
*/
|
||||||
|
while (n > 0) {
|
||||||
|
/*
|
||||||
|
* The largest element is at position 0. Put it at the top,
|
||||||
|
* and swap the arbitrary element from that position into
|
||||||
|
* position 0.
|
||||||
|
*/
|
||||||
|
n--;
|
||||||
|
SWAP(backedges[0], backedges[n]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now repeatedly move that arbitrary element down the heap
|
||||||
|
* by swapping it with the more suitable of its children.
|
||||||
|
*/
|
||||||
|
i = 0;
|
||||||
|
while (1) {
|
||||||
|
int lc, rc;
|
||||||
|
|
||||||
|
lc = LCHILD(i);
|
||||||
|
rc = RCHILD(i);
|
||||||
|
|
||||||
|
if (lc >= n)
|
||||||
|
break; /* we've hit bottom */
|
||||||
|
|
||||||
|
if (rc >= n) {
|
||||||
|
/*
|
||||||
|
* Special case: there is only one child to check.
|
||||||
|
*/
|
||||||
|
if (LESS(backedges[i], backedges[lc]))
|
||||||
|
SWAP(backedges[i], backedges[lc]);
|
||||||
|
|
||||||
|
/* _Now_ we've hit bottom. */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The common case: there are two children and we
|
||||||
|
* must check them both.
|
||||||
|
*/
|
||||||
|
if (LESS(backedges[i], backedges[lc]) ||
|
||||||
|
LESS(backedges[i], backedges[rc])) {
|
||||||
|
/*
|
||||||
|
* Pick the more appropriate child to swap with
|
||||||
|
* (i.e. the one which would want to be the
|
||||||
|
* parent if one were above the other - as one
|
||||||
|
* is about to be).
|
||||||
|
*/
|
||||||
|
if (LESS(backedges[lc], backedges[rc])) {
|
||||||
|
SWAP(backedges[i], backedges[rc]);
|
||||||
|
i = rc;
|
||||||
|
} else {
|
||||||
|
SWAP(backedges[i], backedges[lc]);
|
||||||
|
i = lc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* This element is in the right place; we're done. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LESS
|
||||||
|
#undef PARENT
|
||||||
|
#undef LCHILD
|
||||||
|
#undef RCHILD
|
||||||
|
#undef SWAP
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxflow(int nv, int source, int sink,
|
||||||
|
int ne, const int *edges, const int *capacity,
|
||||||
|
int *flow, int *cut)
|
||||||
|
{
|
||||||
|
void *scratch;
|
||||||
|
int *backedges;
|
||||||
|
int size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate the space.
|
||||||
|
*/
|
||||||
|
size = ne * sizeof(int) + maxflow_scratch_size(nv);
|
||||||
|
backedges = smalloc(size);
|
||||||
|
if (!backedges)
|
||||||
|
return -1;
|
||||||
|
scratch = backedges + ne;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the backedges array.
|
||||||
|
*/
|
||||||
|
maxflow_setup_backedges(ne, edges, backedges);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the main function.
|
||||||
|
*/
|
||||||
|
ret = maxflow_with_scratch(scratch, nv, source, sink, ne, edges,
|
||||||
|
backedges, capacity, flow, cut);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the scratch space.
|
||||||
|
*/
|
||||||
|
sfree(backedges);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And we're done.
|
||||||
|
*/
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TESTMODE
|
||||||
|
|
||||||
|
#define MAXEDGES 256
|
||||||
|
#define MAXVERTICES 128
|
||||||
|
#define ADDEDGE(i,j) do{edges[ne*2] = (i); edges[ne*2+1] = (j); ne++;}while(0)
|
||||||
|
|
||||||
|
int compare_edge(const void *av, const void *bv)
|
||||||
|
{
|
||||||
|
const int *a = (const int *)av;
|
||||||
|
const int *b = (const int *)bv;
|
||||||
|
|
||||||
|
if (a[0] < b[0])
|
||||||
|
return -1;
|
||||||
|
else if (a[0] > b[0])
|
||||||
|
return +1;
|
||||||
|
else if (a[1] < b[1])
|
||||||
|
return -1;
|
||||||
|
else if (a[1] > b[1])
|
||||||
|
return +1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int edges[MAXEDGES*2], ne, nv;
|
||||||
|
int capacity[MAXEDGES], flow[MAXEDGES], cut[MAXVERTICES];
|
||||||
|
int source, sink, p, q, i, j, ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use this algorithm to find a maximal complete matching in a
|
||||||
|
* bipartite graph.
|
||||||
|
*/
|
||||||
|
ne = 0;
|
||||||
|
nv = 0;
|
||||||
|
source = nv++;
|
||||||
|
p = nv;
|
||||||
|
nv += 5;
|
||||||
|
q = nv;
|
||||||
|
nv += 5;
|
||||||
|
sink = nv++;
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
capacity[ne] = 1;
|
||||||
|
ADDEDGE(source, p+i);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
capacity[ne] = 1;
|
||||||
|
ADDEDGE(q+i, sink);
|
||||||
|
}
|
||||||
|
j = ne;
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+0,q+0);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+1,q+0);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+1,q+1);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+2,q+1);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+2,q+2);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+3,q+2);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+3,q+3);
|
||||||
|
capacity[ne] = 1; ADDEDGE(p+4,q+3);
|
||||||
|
/* capacity[ne] = 1; ADDEDGE(p+2,q+4); */
|
||||||
|
qsort(edges, ne, 2*sizeof(int), compare_edge);
|
||||||
|
|
||||||
|
ret = maxflow(nv, source, sink, ne, edges, capacity, flow, cut);
|
||||||
|
|
||||||
|
printf("ret = %d\n", ret);
|
||||||
|
|
||||||
|
for (i = 0; i < ne; i++)
|
||||||
|
printf("flow %d: %d -> %d\n", flow[i], edges[2*i], edges[2*i+1]);
|
||||||
|
|
||||||
|
for (i = 0; i < nv; i++)
|
||||||
|
if (cut[i] == 0)
|
||||||
|
printf("difficult set includes %d\n", i);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
95
maxflow.h
Normal file
95
maxflow.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Edmonds-Karp algorithm for finding a maximum flow and minimum
|
||||||
|
* cut in a network. Almost identical to the Ford-Fulkerson
|
||||||
|
* algorithm, but apparently using breadth-first search to find the
|
||||||
|
* _shortest_ augmenting path is a good way to guarantee
|
||||||
|
* termination and ensure the time complexity is not dependent on
|
||||||
|
* the actual value of the maximum flow. I don't understand why
|
||||||
|
* that should be, but it's claimed on the Internet that it's been
|
||||||
|
* proved, and that's good enough for me. I prefer BFS to DFS
|
||||||
|
* anyway :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAXFLOW_MAXFLOW_H
|
||||||
|
#define MAXFLOW_MAXFLOW_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The actual algorithm.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
*
|
||||||
|
* - `scratch' is previously allocated scratch space of a size
|
||||||
|
* previously determined by calling `maxflow_scratch_size'.
|
||||||
|
*
|
||||||
|
* - `nv' is the number of vertices. Vertices are assumed to be
|
||||||
|
* numbered from 0 to nv-1.
|
||||||
|
*
|
||||||
|
* - `source' and `sink' are the distinguished source and sink
|
||||||
|
* vertices.
|
||||||
|
*
|
||||||
|
* - `ne' is the number of edges in the graph.
|
||||||
|
*
|
||||||
|
* - `edges' is an array of 2*ne integers, giving a (source, dest)
|
||||||
|
* pair for each network edge. Edge pairs are expected to be
|
||||||
|
* sorted in lexicographic order.
|
||||||
|
*
|
||||||
|
* - `backedges' is an array of `ne' integers, each a distinct
|
||||||
|
* index into `edges'. The edges in `edges', if permuted as
|
||||||
|
* specified by this array, should end up sorted in the _other_
|
||||||
|
* lexicographic order, i.e. dest taking priority over source.
|
||||||
|
*
|
||||||
|
* - `capacity' is an array of `ne' integers, giving a maximum
|
||||||
|
* flow capacity for each edge. A negative value is taken to
|
||||||
|
* indicate unlimited capacity on that edge, but note that there
|
||||||
|
* may not be any unlimited-capacity _path_ from source to sink
|
||||||
|
* or an assertion will be failed.
|
||||||
|
*
|
||||||
|
* Output:
|
||||||
|
*
|
||||||
|
* - `flow' must be non-NULL. It is an array of `ne' integers,
|
||||||
|
* each giving the final flow along each edge.
|
||||||
|
*
|
||||||
|
* - `cut' may be NULL. If non-NULL, it is an array of `nv'
|
||||||
|
* integers, which will be set to zero or one on output, in such
|
||||||
|
* a way that:
|
||||||
|
* + the set of zero vertices includes the source
|
||||||
|
* + the set of one vertices includes the sink
|
||||||
|
* + the maximum flow capacity between the zero and one vertex
|
||||||
|
* sets is achieved (i.e. all edges from a zero vertex to a
|
||||||
|
* one vertex are at full capacity, while all edges from a
|
||||||
|
* one vertex to a zero vertex have no flow at all).
|
||||||
|
*
|
||||||
|
* - the returned value from the function is the total flow
|
||||||
|
* achieved.
|
||||||
|
*/
|
||||||
|
int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
|
||||||
|
int ne, const int *edges, const int *backedges,
|
||||||
|
const int *capacity, int *flow, int *cut);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The above function expects its `scratch' and `backedges'
|
||||||
|
* parameters to have already been set up. This allows you to set
|
||||||
|
* them up once and use them in multiple invocates of the
|
||||||
|
* algorithm. Now I provide functions to actually do the setting
|
||||||
|
* up.
|
||||||
|
*/
|
||||||
|
int maxflow_scratch_size(int nv);
|
||||||
|
void maxflow_setup_backedges(int ne, const int *edges, int *backedges);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simplified version of the above function. All parameters are the
|
||||||
|
* same, except that `scratch' and `backedges' are constructed
|
||||||
|
* internally. This is the simplest way to call the algorithm as a
|
||||||
|
* one-off; however, if you need to call it multiple times on the
|
||||||
|
* same network, it is probably better to call the above version
|
||||||
|
* directly so that you only construct `scratch' and `backedges'
|
||||||
|
* once.
|
||||||
|
*
|
||||||
|
* Additional return value is now -1, meaning that scratch space
|
||||||
|
* could not be allocated.
|
||||||
|
*/
|
||||||
|
int maxflow(int nv, int source, int sink,
|
||||||
|
int ne, const int *edges, const int *capacity,
|
||||||
|
int *flow, int *cut);
|
||||||
|
|
||||||
|
#endif /* MAXFLOW_MAXFLOW_H */
|
2
osx.m
2
osx.m
@ -1341,6 +1341,8 @@ static void osx_draw_text(void *handle, int x, int y, int fonttype,
|
|||||||
point.x -= size.width / 2;
|
point.x -= size.width / 2;
|
||||||
if (align & ALIGN_VCENTRE)
|
if (align & ALIGN_VCENTRE)
|
||||||
point.y -= size.height / 2;
|
point.y -= size.height / 2;
|
||||||
|
else
|
||||||
|
point.y -= size.height;
|
||||||
|
|
||||||
[string drawAtPoint:point withAttributes:attr];
|
[string drawAtPoint:point withAttributes:attr];
|
||||||
}
|
}
|
||||||
|
52
puzzles.but
52
puzzles.but
@ -1802,6 +1802,58 @@ These parameters are available from the \q{Custom...} option on the
|
|||||||
\dd Size of grid in squares.
|
\dd Size of grid in squares.
|
||||||
|
|
||||||
|
|
||||||
|
\C{tents} \i{Tents}
|
||||||
|
|
||||||
|
\cfg{winhelp-topic}{games.tents}
|
||||||
|
|
||||||
|
You have a grid of squares, some of which contain trees. Your aim is
|
||||||
|
to place tents in some of the remaining squares, in such a way that
|
||||||
|
the following conditions are met:
|
||||||
|
|
||||||
|
\b There are exactly as many tents as trees.
|
||||||
|
|
||||||
|
\b The tents and trees can be matched up in such a way that each
|
||||||
|
tent is directly adjacent (horizontally or vertically, but not
|
||||||
|
diagonally) to its own tree. However, a tent may be adjacent to
|
||||||
|
other trees as well as its own.
|
||||||
|
|
||||||
|
\b No two tents are adjacent horizontally, vertically \e{or
|
||||||
|
diagonally}.
|
||||||
|
|
||||||
|
\b The number of tents in each row, and in each column, matches the
|
||||||
|
numbers given round the sides of the grid.
|
||||||
|
|
||||||
|
This puzzle can be found in several places on the Internet, and was
|
||||||
|
brought to my attention by e-mail. I don't know who I should credit
|
||||||
|
for inventing it.
|
||||||
|
|
||||||
|
\H{tents-controls} \i{Tents controls}
|
||||||
|
|
||||||
|
\IM{Tents controls} controls, for Tents
|
||||||
|
|
||||||
|
Left-clicking in a blank square will place a tent in it.
|
||||||
|
Right-clicking in a blank square will colour it green, indicating
|
||||||
|
that you are sure it \e{isn't} a tent. Clicking either button in an
|
||||||
|
occupied square will clear it.
|
||||||
|
|
||||||
|
(All the actions described in \k{common-actions} are also available.)
|
||||||
|
|
||||||
|
\H{tents-parameters} \I{parameters, for Tents}Tents parameters
|
||||||
|
|
||||||
|
These parameters are available from the \q{Custom...} option on the
|
||||||
|
\q{Type} menu.
|
||||||
|
|
||||||
|
\dt \e{Width}, \e{Height}
|
||||||
|
|
||||||
|
\dd Size of grid in squares.
|
||||||
|
|
||||||
|
\dt \e{Difficulty}
|
||||||
|
|
||||||
|
\dd Controls the difficulty of the generated puzzle. More difficult
|
||||||
|
puzzles require more complex deductions, but at present none of the
|
||||||
|
available difficulty levels requires guesswork or backtracking.
|
||||||
|
|
||||||
|
|
||||||
\A{licence} \I{MIT licence}\ii{Licence}
|
\A{licence} \I{MIT licence}\ii{Licence}
|
||||||
|
|
||||||
This software is \i{copyright} 2004-2005 Simon Tatham.
|
This software is \i{copyright} 2004-2005 Simon Tatham.
|
||||||
|
Reference in New Issue
Block a user