mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -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
|
||||
LOOPY = loopy tree234 dsf
|
||||
LIGHTUP = lightup combi
|
||||
TENTS = tents maxflow
|
||||
|
||||
ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle
|
||||
+ MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox SLANT
|
||||
+ LIGHTUP MAP LOOPY inertia
|
||||
+ LIGHTUP MAP LOOPY inertia TENTS
|
||||
|
||||
GTK = gtk printing ps
|
||||
|
||||
@ -55,6 +56,7 @@ lightup : [X] GTK COMMON LIGHTUP
|
||||
map : [X] GTK COMMON MAP
|
||||
loopy : [X] GTK COMMON LOOPY
|
||||
inertia : [X] GTK COMMON inertia
|
||||
tents : [X] GTK COMMON TENTS
|
||||
|
||||
# Auxiliary command-line programs.
|
||||
STANDALONE = nullfe random misc malloc
|
||||
@ -65,6 +67,7 @@ mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] tree234 STANDALONE
|
||||
slantsolver : [U] slant[STANDALONE_SOLVER] dsf STANDALONE
|
||||
mapsolver : [U] map[STANDALONE_SOLVER] dsf STANDALONE m.lib
|
||||
lightupsolver : [U] lightup[STANDALONE_SOLVER] combi STANDALONE
|
||||
tentssolver : [U] tents[STANDALONE_SOLVER] maxflow STANDALONE
|
||||
|
||||
solosolver : [C] solo[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
|
||||
mapsolver : [C] map[STANDALONE_SOLVER] dsf 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
|
||||
# already has a reasonably important utility program by that name!
|
||||
@ -97,6 +101,7 @@ lightup : [G] WINDOWS COMMON LIGHTUP
|
||||
map : [G] WINDOWS COMMON MAP
|
||||
loopy : [G] WINDOWS COMMON LOOPY
|
||||
inertia : [G] WINDOWS COMMON inertia
|
||||
tents : [G] WINDOWS COMMON TENTS
|
||||
|
||||
# Mac OS X unified application containing all the puzzles.
|
||||
Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
|
||||
@ -189,7 +194,7 @@ install:
|
||||
for i in cube net netslide fifteen sixteen twiddle \
|
||||
pattern rect solo mines samegame flip guess \
|
||||
pegs dominosa untangle blackbox slant lightup \
|
||||
map loopy inertia; do \
|
||||
map loopy inertia tents; do \
|
||||
$(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i \
|
||||
|| exit 1; \
|
||||
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)
|
||||
rect.y -= rect.height / 2;
|
||||
else
|
||||
rect.y -= rect.height;
|
||||
|
||||
if (align & ALIGN_HCENTRE)
|
||||
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);
|
||||
if (align & ALIGN_VCENTRE)
|
||||
y += asc - (asc+desc)/2;
|
||||
else
|
||||
y += asc;
|
||||
|
||||
/*
|
||||
* ... 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 slant;
|
||||
extern const game solo;
|
||||
extern const game tents;
|
||||
extern const game twiddle;
|
||||
extern const game untangle;
|
||||
|
||||
@ -61,6 +62,7 @@ const game *gamelist[] = {
|
||||
&sixteen,
|
||||
&slant,
|
||||
&solo,
|
||||
&tents,
|
||||
&twiddle,
|
||||
&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;
|
||||
if (align & ALIGN_VCENTRE)
|
||||
point.y -= size.height / 2;
|
||||
else
|
||||
point.y -= size.height;
|
||||
|
||||
[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.
|
||||
|
||||
|
||||
\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}
|
||||
|
||||
This software is \i{copyright} 2004-2005 Simon Tatham.
|
||||
|
Reference in New Issue
Block a user