I am looking at the classical example for RecursiveTask calculating Fibonacci numbers. I added some output: see http://jboston.net/2017/FibonacciOutp.txt, the code is below
Still cannot understand how this is working, why first we see all numbers decreasing from 12 and then repeating many times
number=2 fcal1.join()=1 fcal2.compute()=0
number=3 fcal1.join()=1 fcal2.compute()=1
The code:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class RecursiveTaskDemo {
public static void main(String[] args) {
FibonacciCal fibonacciCal = new FibonacciCal(12);
ForkJoinPool pool = new ForkJoinPool();
int i = pool.invoke(fibonacciCal);
System.out.println(i);
}
}
class FibonacciCal extends RecursiveTask<Integer> {
private static final long serialVersionUID = 1L;
final int num;
FibonacciCal(int num) {
this.num = num;
}
#Override
protected Integer compute() {
if (num <= 1) {
return num;
}
System.out.println("number=" + num);
FibonacciCal fcal1 = new FibonacciCal(num - 1);
fcal1.fork();
FibonacciCal fcal2 = new FibonacciCal(num - 2);
int fcal1Join = fcal1.join();
int fcal2Compute = fcal2.compute();
System.out.println("number=" + num + " fcal1.join()=" + fcal1Join + " fcal2.compute()=" + fcal2Compute);
return fcal2Compute + fcal1Join;
}
}
When FibonacciCal::compute is called, it forks off one thread to calculate fib(n - 1) and computes fib(n - 2) in the starting thread. The branching looks a bit like this (fib(n) represents a thread running FibonacciCal(n).compute()):
STARTING WITH pool.invoke(new FibonacciCal(5)):
fib(5)
A BIT LATER:
fib(5) === fib(3) // The fibcal2.compute() call, printing num = 3
\== fib(4) // The fibcal1.fork() call, printing num = 4
LATER:
fib(5) === fib(3) === fib(1) // These fib(0/1)s are base cases and will start folding the tree back up
| \== fib(2) === fib(0) // Will return 1 and not fork
| \== fib(1) // Will return 1 and not fork
\== fib(4) === fib(2) === fib(0)
| \== fib(1)
\== fib(3) === fib(1)
\== fib(2) === fib(0)
\== fib(1)
METHODS START RETURNING:
fib(5) === fib(3) === 1
| \== fib(2) === 1
| \== 1
\== fib(4) === fib(2) === 1
| \== 1
\== fib(3) === 1
\== fib(2) === 1
\== 1
ADDITIONS START HAPPENING:
fib(5) === fib(3) === 1
| \== (1 + 1) = 2 // When a thread joins its child, it prints out its number again.
| // Since the tree is now folding instead of unfolding, the printlns appear, approximately, the opposite order
\== fib(4) === (1 + 1) = 2
\== fib(3) === 1
\== (1 + 1) = 2
LATER:
fib(5) === (1 + 2) = 3 === 1
| \== 2
\== fib(4) === 2
\== (1 + 2) = 3 === 1
\== 2
END:
8 === 3 === 1
| \== 2
\== 5 === 2
\== 3 === 1
\== 2
The reason you get a lot of repeating numbers is because there isn't any memoization. In this example with fib(5) you see that you get 8 base terms of fib(0) or fib(1), 3 terms of fib(2), 2 of fib(3), and one fib(4). As the lower order terms start joining their children you get lots of printlns with small nums, until the end comes and they start counting back up.
Related
I'm having trouble showing how to ensure recursively decreasing functions on a tree class in Dafny. I have the following definitions which verify.
class RoseTree {
var NodeType: int
var id: string
var children: array<RoseTree>
ghost var nodeSet: set<RoseTree>
constructor(nt: int, id: string, children: array<RoseTree>)
ensures forall x :: 0 <= x < children.Length ==> children[x].nodeSet <= this.nodeSet
ensures forall x :: 0 <= x < this.children.Length ==> this.children[x].nodeSet <= this.nodeSet
{
this.NodeType := nt;
this.id := id;
this.children := children;
if children.Length == 0 {
this.nodeSet := {this};
}else{
this.nodeSet := {this}+childrenNodeSet(children);
}
}
}
function setRosePick(s: set<set<RoseTree>>): set<RoseTree>
requires s != {}
{
var x :| x in s; x
}
function setUnion(setosets: set<set<RoseTree>>) : set<RoseTree>
decreases setosets
{
if setosets == {} then {} else
var x := setRosePick(setosets);
assert x <= x + setUnion(setosets-{x});
x + setUnion(setosets-{x})
}
lemma setUnionDef(s: set<set<RoseTree>>, y: set<RoseTree>)
requires y in s
ensures setUnion(s) == y + setUnion(s - {y})
{
var x := setRosePick(s);
if y == x {
}else{
calc {
setUnion(s);
==
x + setUnion(s - {x});
== {setUnionDef(s - {x}, y); }
x + y + setUnion(s - {x} - {y});
== { assert s - {x} - {y} == s - {y} - {x}; }
y + x + setUnion(s - {y} - {x});
== {setUnionDef(s - {y}, x); }
y + setUnion(s - {y});
}
}
}
lemma setUnionReturns(s: set<set<RoseTree>>)
ensures s == {} ==> setUnion(s) == {}
ensures s != {} ==> forall x :: x in s ==> x <= setUnion(s)
{
if s == {} {
assert setUnion(s) == {};
} else {
forall x | x in s
ensures x <= setUnion(s)
{
setUnionDef(s, x);
assert x <= x + setUnion(s-{x});
}
}
}
function childNodeSets(children: array<RoseTree>): set<set<RoseTree>>
reads children
reads set x | 0 <= x < children.Length :: children[x]
{
set x | 0 <= x < children.Length :: children[x].nodeSet
}
function childNodeSetsPartial(children: array<RoseTree>, index: int): set<set<RoseTree>>
requires 0 <= index < children.Length
reads children
reads set x | index <= x < children.Length :: children[x]
{
set x | index <= x < children.Length :: children[x].nodeSet
}
function childrenNodeSet(children: array<RoseTree>): set<RoseTree>
reads children
reads set x | 0 <= x < children.Length :: children[x]
ensures forall x :: x in childNodeSets(children) ==> x <= childrenNodeSet(children)
ensures forall i :: 0 <= i < children.Length ==> children[i].nodeSet <= childrenNodeSet(children)
{
var y := childNodeSets(children);
setUnionReturns(y);
setUnion(y)
}
In particular I'm trying to define the height function for the tree.
function height(node: RoseTree):nat
reads node
reads node.children
reads set x | 0 <= x < node.children.Length :: node.children[x]
decreases node.nodeSet
{
if node.children.Length == 0 then 1 else 1 + maxChildHeight(node, node.children,node.children.Length-1,0)
}
function maxChildHeight(node: RoseTree, children: array<RoseTree>, index: nat, best: nat) : nat
reads node
reads node.children
reads set x | 0 <= x < node.children.Length :: node.children[x]
requires children == node.children
requires 0 <= index < children.Length
ensures forall x :: 0 <= x <= index < children.Length ==> maxChildHeight(node, children, index, best) >= height(children[x])
decreases node.nodeSet - setUnion(childNodeSetsPartial(children, index)), 1
{
if index == 0 then best else if height(children[index]) >= best then maxChildHeight(node, children, index-1, height(children[index])) else maxChildHeight(node, children, index-1, best)
}
I though it should be possible to show that the nodeSet of the node will be a subset of its parent node or that the union of child node sets will be a subset of the parent node, and thus both functions will terminate. My decreases expressions don't prove it to dafny and I'm not quite sure how to proceed. Is there another way to prove termination or can I fix these decrease statements?
Also, do all instances of a class have the constructor ensure statements applied implicitly or only if explicitly constructed using the constructor?
Edit: updated definitions of childNodeSetsPartial and maxChildHeight
to recurse downward. It still doesn't verify.
Defining mutable linked heap-allocated data structures in Dafny is not very common except as an exercise. So you should consider whether a datatype would serve you better, as in
datatype RoseTree = Node(children: seq<RoseTree>)
function height(r: RoseTree): int
{
if r.children == [] then
1
else
var c := set i | 0 <= i < |r.children| :: height(r.children[i]);
assert height(r.children[0]) in c;
assert c != {};
SetMax(c) + 1
}
If you insist on mutable linked heap-allocated data structures, then there is a standard idiom for doing that. Please read sections 0 and 1 of these lecture notes and check out the modern version of the example code here.
Applying this idiom to your code, we get the following.
class RoseTree {
var NodeType: int
var id: string
var children: array<RoseTree>
ghost var repr: set<object>
predicate Valid()
reads this, repr
decreases repr
{
&& this in repr
&& children in repr
&& (forall i | 0 <= i < children.Length ::
children[i] in repr
&& children[i].repr <= repr
&& this !in children[i].repr
&& children[i].Valid())
}
constructor(nt: int, id: string, children: array<RoseTree>)
requires forall i | 0 <= i < children.Length :: children[i].Valid()
ensures Valid()
{
this.NodeType := nt;
this.id := id;
this.children := children;
this.repr := {this, children} +
(set i | 0 <= i < children.Length :: children[i]) +
(set x, i | 0 <= i < children.Length && x in children[i].repr :: x);
}
}
function SetMax(s: set<int>): int
requires s != {}
ensures forall x | x in s :: SetMax(s) >= x
{
var x :| x in s;
if s == {x} then
x
else
var y := SetMax(s - {x});
assert forall z | z in s :: z == x || (z in (s - {x}) && y >= z);
if x > y then x else y
}
function height(node: RoseTree): nat
requires node.Valid()
reads node.repr
{
if node.children.Length == 0 then
1
else
var c := set i | 0 <= i < node.children.Length :: height(node.children[i]);
assert height(node.children[0]) in c;
assert c != {};
SetMax(c) + 1
}
do all instances of a class have the constructor ensure statements applied implicitly or only if explicitly constructed using the constructor?
I'm not sure if I understand this question. I think the answer is "no", though. Since a class might have multiple constructors with different postconditions.
So I was writing a rock paper scissors game when I came to writing this function:
a is player one's move, b is player two's move. All I need to figure out is if player one won, lost, or tied.
//rock=0, paper=1, scissors=2
processMove(a, b) {
if(a == b) ties++;
else {
if(a==0 && b==2) wins++;
else if(a==0 && b==1) losses++;
else if(a==1 && b==2) losses++;
else if(a==1 && b==0) wins++;
else if(a==2 && b==1) wins++;
else if(a==2 && b==0) losses++;
}
}
My question is: What's the most elegant way this function can be written?
Edit: I'm looking for a one-liner.
if (a == b) ties++;
else if ((a - b) % 3 == 1) wins++;
else losses++;
I need to know exactly which language you are using to turn it into a strictly one-liner...
For JavaScript (or other languages with strange Modulus) use:
if (a == b) ties++;
else if ((a - b + 3) % 3 == 1) wins++;
else losses++;
A 3x3 matrix would be "more elegant", I suppose.
char result = "TWLLTWWLT".charAt(a * 3 + b);
(Edited: Forgot that a and b were already zero-origin.)
I suppose you could use the terniary operator like this -
if (b==0) a==1? wins++ : loss++;
if (b==1) a==1? loss++ : wins++;
if (b==2) a==1? loss++ : wins++;
You can do it with a simple mathematical formula to get the result and then compare with if like this:
var moves = {
'rock': 0,
'paper': 1,
'scissors': 2
};
var result = {
'wins': 0,
'losses': 0,
'ties': 0
};
var processMove = function (a, b) {
var processResult = (3 + b - a) % 3;
if (!processResult) {
++result['ties'];
} else if(1 == processResult) {
++result['losses'];
} else {
++result['wins'];
}
return result;
};
jsFiddle Demo
One line processMove function without return:
var processMove = function (a, b) {
((3 + b - a) % 3) ? 1 == ((3 + b - a) % 3) ? ++result.losses : ++result.wins : ++result.ties;
};
how do you do it in java?
result = (comp - x ) % 3 ;
System.out.println (result);
if (result == 0 )// if the game is tie
{
System.out.println ("A Tie!") ;
}
else if (result == 1 || result == 2 )
{
//System.out.println (user + " " + "beats" + " " + computer_choice + " you win" );
System.out.println ("comp win");
}
else
{
System.out.println ("you win");
//System.out.println (computer_choice + " " + "beats" + " " + user + "you lose");
}
I've got the following grid of numbers centered around 0 and increasing in spiral. I need an algorithm which would receive number in spiral and return x; y - numbers of moves how to get to that number from 0. For example for number 9 it would return -2; -1. For 4 it would be 1; 1.
25|26|... etc.
24| 9|10|11|12
23| 8| 1| 2|13
22| 7| 0| 3|14
21| 6| 5| 4|15
20|19|18|17|16
This spiral can be slightly changed if it would help the algorithm to be better.
Use whatever language you like. I would really appreciate mathematical explanation.
Thank you.
First we need to determine which cycle (distance from center) and sector (north, east, south or west) we are in. Then we can determine the exact position of the number.
The first numbers in each cycle is as follows: 1, 9, 25
This is a quadratic sequence: first(n) = (2n-1)^2 = 4n^2 - 4n + 1
The inverse of this is the cycle-number: cycle(i) = floor((sqrt(i) + 1) / 2)
The length of a cycle is: length(n) = first(n+1) - first(n) = 8n
The sector will then be:
sector(i) = floor(4 * (i - first(cycle(i))) / length(cycle(i)))
Finally, to get the position, we need to extrapolate from the position of the first number in the cycle and sector.
To put it all together:
def first(cycle):
x = 2 * cycle - 1
return x * x
def cycle(index):
return (isqrt(index) + 1)//2
def length(cycle):
return 8 * cycle
def sector(index):
c = cycle(index)
offset = index - first(c)
n = length(c)
return 4 * offset / n
def position(index):
c = cycle(index)
s = sector(index)
offset = index - first(c) - s * length(c) // 4
if s == 0: #north
return -c, -c + offset + 1
if s == 1: #east
return -c + offset + 1, c
if s == 2: #south
return c, c - offset - 1
# else, west
return c - offset - 1, -c
def isqrt(x):
"""Calculates the integer square root of a number"""
if x < 0:
raise ValueError('square root not defined for negative numbers')
n = int(x)
if n == 0:
return 0
a, b = divmod(n.bit_length(), 2)
x = 2**(a+b)
while True:
y = (x + n//x)//2
if y >= x:
return x
x = y
Example:
>>> position(9)
(-2, -1)
>>> position(4)
(1, 1)
>>> position(123456)
(-176, 80)
Do you mean something like this? I did not implement any algorithm and the code can be written better but it works - that's always a start :) Just change the threshold value for whatever you wish and you'll get the result.
static int threshold=14, x=0, y=0;
public static void main(String[] args) {
int yChange=1, xChange=1, count=0;
while( !end(count) ){
for (int i = 0; i < yChange; i++) {
if( end(count) )return;
count++;
y--;
}
yChange++;
for (int i = 0; i < xChange; i++) {
if( end(count) )return;
count++;
x++;
}
xChange++;
for (int i = 0; i < yChange; i++) {
if( end(count) )return;
count++;
y++;
}
yChange++;
for (int i = 0; i < xChange; i++) {
if( end(count) )return;
count++;
x--;
}
xChange++;
}
}
public static boolean end(int count){
if(count<threshold){
return false;
}else{
System.out.println("count: "+count+", x: "+x+", y: "+y);
return true;
}
}
How do you fake conditionals that don't nest in a language that doesn't allow goto? I want to do the following:
if (condition1)
action1;
if (!condition1 && condition2)
action2;
if (!condition2 && condition3)
action3;
Without:
Needlessly evaluating any condition more than once.
Needlessly storing the result of any such evaluation in a variable.
Needlessly specifying more than once that any action should be performed.
The original snippet fails to meet requirement 1.
The following snippet fails to meet requirement 2:
if (condition1) {
action1;
c2 = false;
}
else if (c2 = condition2)
action2;
if (!c2 && condition3)
action3;
And the following snippet fails to meet requirement 3:
if (condition1) {
action1;
if (condition3)
action3;
}
else if (condition2)
action2;
else if (condition3)
action3;
EDIT:
It is impossible that condition1 and condition2 be true simultaneously.
It is impossible that condition2 and condition3 be true simultaneously.
Here is the original code (in JavaScript):
// If only the array of elements was specified,
// wrap it inside an object.
if (info.constructor !== Object)
info = {children: info};
// If no array of elements was specified, create
// an empty array of elements.
if (!info.children)
info.children = [];
// If, instead of an array of elements, we have
// a single element, wrap it inside an array.
if (info.children.constructor !== Array)
info.children = [info.children];
Truth Table
C1 C2 C3 Action
0 0 0 None
0 0 1 A3
0 1 0 A2
0 1 1 A2
1 0 0 A1
1 0 1 A1+A3
1 1 0 A1
1 1 1 A1
Is switch/case against the rules? :)
switch(C1*4 + C2*2 + C1) {
case 7: case 6: case 4: A1; break;
case 5: A1; A3; break;
case 3: case 2: A2; break;
case 1: A3; break;
}
So how do you do this with goto anyway? This immediately comes to mind, but doesn't have quite the same result:
if(condition1) {
action1;
goto a;
}
if(condition2) {
action2;
goto b;
}
a:
if(condition3) {
//This will run if condition1 && condition2 && condition3
action3;
}
b:
Either way, you do have a few tricks that can break out of nested "if"s. Try:
do {
if(condition1) {
action1;
} elseif(condition2) {
action2;
break;
}
if(condition3) {
action3;
}
} while(false);
It's essentially a goto, but...
That version will duplicate the goto construct that I imagined, rather than the one in the OP. Note that "return" works about the same, in case that looks cleaner in your code (and it might be able to push the hack a bit further by returning a boolean).
How to get the co-ordinates of the outline shape formed using smaller grid blocks.
For example, If I used 32x32 unit blocks for construct a shape (any shape).
Then how can I get overall co-ordinates of the shape, including the negative spaces.
For example:
One could arrange the blocks like this:
(each block is 32x32 and coordinates refer to bottom left corner of the block)
Block 1 - (0,0)
BLock 2 - (32,0)
Block 3 - 64,0)
Block 4 - (64,32)
Block 5 - (64, 64)
BLock 6 - (32, 64)
BLock 6 - (0 64)
Block 7 - (0, 32)
Now you can see this will create an empty space in the middle.
So what I would like to know is, how to get the coordinates of the above shape such that I get:
Main Block = (0,0), (96,0), (0,96)
Empty space = (32,32), (64,32), (64,64), (32,64)
Is there any mathematical solution to this?
Eventually I will be doing complex shapes.
thanks
******** edit ****
Hi,
How to deal with this condition?
<------------------^<----^
| || |
V------------------>| |
<------^ /^| |
| |<------^ / || |
| || |/ || |
V------>V------>V-->V---->
i would like the result to be like this
<-------------------<----^
| |
V ^-----------> |
| | / |
| <-------^ / |
| |/ |
V------>------->--->----->
Think of each square as an outline comprised of four vectors going in a counter-clockwise chain.
<-----^
| |
| |
V----->
So for all the squares in your shape, take the union of their outline vectors. If two outline vectors in the union are identical but go in opposite directions, they cancel each other out and are removed from the union.
For example, for two squares that are side by side the union is 8 vectors
<-----^<-----^
| || |
| || |
V----->V----->
which reduces to 6 vectors because the two vertical vectors in the middle cancel:
<-----<-----^
| |
| |
V----->----->
For the example you gave, the result of this will be (after cancellations):
<-----<-----<-----^
| |
| |
V ^-----> ^
| | | |
| | | |
V <-----V ^
| |
| |
V----->----->----->
You just have to connect up the vectors in the final reduced union to read off the outline paths. Note that the inner outline ("the hole") runs clockwise.
You would probably need to focus on Boolean Polygon Operations, unions in your case. There are plenty of papers covering the 2D boolean polygon operations and constructive planar geometry, see the Wikipedia: http://en.wikipedia.org/wiki/Boolean_operations_on_polygons.
I know this is very late to the discussion, but I recently had to deal with the same problem, and came up with the following algorithm, described in a somewhat high level here.
First, some terminology. As seen in the picture, we label the top left cell "r0c0" (i.e. row 0 column 0), and the one to the right "r0c1" and so on. We say that the edge to the left of rxcy starts at x,y and goes to x,(y+1) and so on.
The idea is to first find the points at which the outline should change direction.
That is the corners of the shape. I did this by first making a 2d array of numbers where the number was 1 if there was a cell in that place, and 0 otherwise.
Then I looped over the 2d array, and found that the top left point of a cell should be included if either the cell above and the cell to the right were both not there, or if they were both there and the cell above and to the left was not there. Similarly for the three other corners of the cell. We also remember to label the points according to what intercardinal direction they are in their cell (north west, north east and so on)
After this we have a list of points. We then start by finding the top left point of those and traversing. We know to start going right after the top left point. Using the image, we go right from a north west point. We then find the point that has the same y coordinate, but an x-coordinate larger, but the least largest of the points to the right. We also know that the next corner we hit should have an intercardinal direction of north west or north east, as we can't go from north to west by going right.
In the picture we would hit the north east point at r3c6. We then know to go down after that, because going to a north east point from the right means going down afterwards. We continue like this until we can find no more points.
There might still be points left after all this. This means we have disjoint cells, or that there is a hole. So just do the whole thing again.
I get that this wall of text is quite difficult to follow along with, so here is some typescript code, that will hopefully make it a bit simpler (sorry, don't know php). If you have any questions, please reach out. Also, this code can probably be optimized.
The main function is the createOutlines function
type Position = {row: number; column: number};
type Dimensions = { rows: number; columns: number };
type Point = {
x: number;
y: number;
};
type Direction = 'up' | 'left' | 'right' | 'down';
type InterCardinal = 'nw' | 'ne' | 'sw' | 'se';
type OutlinePoint = {
point: Point;
interCardinal: InterCardinal;
};
function findOutlinePoints(
positions: Position[],
dimensions: Dimensions
): OutlinePoint[] {
// Essentially a grid of 1 and undefined where it is 1 if there is a cell in that position
// The JSON.parse(JSON.stringify()) part is just a deep copy, as javascript is quite weird
const matrixOfPoints: (number | undefined)[][] = JSON.parse(JSON.stringify(Array(dimensions.rows).fill([])));
positions.forEach(({ row, column }) => {
matrixOfPoints[row][column] = 1;
});
const points: OutlinePoint[] = [];
for (let rowIndex = 0; rowIndex < dimensions.rows; rowIndex++) {
const row = matrixOfPoints[rowIndex];
if (row.length === 0) {
continue;
}
for (let columnIndex = 0; columnIndex < dimensions.columns; columnIndex++) {
const cell = row[columnIndex];
if (!cell) {
continue;
}
// Find the values of cells around the center cell
const nw = matrixOfPoints[rowIndex - 1]?.[columnIndex - 1];
const n = matrixOfPoints[rowIndex - 1]?.[columnIndex];
const ne = matrixOfPoints[rowIndex - 1]?.[columnIndex + 1];
const w = matrixOfPoints[rowIndex]?.[columnIndex - 1];
const e = matrixOfPoints[rowIndex]?.[columnIndex + 1];
const sw = matrixOfPoints[rowIndex + 1]?.[columnIndex - 1];
const s = matrixOfPoints[rowIndex + 1]?.[columnIndex];
const se = matrixOfPoints[rowIndex + 1]?.[columnIndex + 1];
// Add the points
// Top left point
if ((n == null && w == null) || (n != null && nw == null && w != null)) {
// The north west point of this cell is a corner point, so add this point and specify that it is a north west (nw) point
points.push({
point: { x: columnIndex, y: rowIndex },
interCardinal: 'nw'
});
}
// Top right point
if ((n == null && e == null) || (n != null && ne == null && e != null)) {
points.push({
point: { x: columnIndex + 1, y: rowIndex },
interCardinal: 'ne'
});
}
// Bottom left
if ((w == null && s == null) || (w != null && sw == null && s != null)) {
points.push({
point: { x: columnIndex, y: rowIndex + 1 },
interCardinal: 'sw'
});
}
// Bottom right
if ((e == null && s == null) || (e != null && se == null && s != null)) {
points.push({
point: { x: columnIndex + 1, y: rowIndex + 1 },
interCardinal: 'se'
});
}
}
}
return points;
}
// Finds the point that is left most, and of the left most points, the one that is highest. Also finds the index of that point in the list
function findTopLeftOutlinePoint(
outlinePoints: OutlinePoint[]
): [OutlinePoint | undefined, number] {
let topLeftPoint: OutlinePoint | undefined = undefined;
let index = -1;
outlinePoints.forEach((p, i) => {
if (topLeftPoint == null) {
topLeftPoint = p;
index = i;
return;
}
if (
p.point.x < topLeftPoint.point.x ||
(p.point.x <= topLeftPoint.point.x &&
p.point.y < topLeftPoint.point.y)
) {
index = i;
topLeftPoint = p;
}
});
return [topLeftPoint, index];
}
/** E.g. going, "up", coming to "nw", one has to go "right" */
const NextDirection: Record<Direction, Record<InterCardinal, Direction>> = {
up: {
nw: 'right',
ne: 'left',
sw: 'left',
se: 'right'
},
down: {
nw: 'left',
ne: 'right',
sw: 'right',
se: 'left'
},
right: {
nw: 'up',
ne: 'down',
sw: 'down',
se: 'up'
},
left: {
nw: 'down',
ne: 'up',
sw: 'up',
se: 'down'
}
};
// Given the previous point, and the direction, find the next point from the list of points
function findNextPoint(
previousPointInPath: OutlinePoint,
points: OutlinePoint[],
direction: Direction
): [OutlinePoint, number] | undefined {
// e.g. if coming from nw going right, we should find a point that has the same y coordinates, and has an interCardinal of ne or se
let nextPointIndex: number | undefined;
let nextPoint: OutlinePoint | undefined;
switch (direction) {
case 'right':
// We are going "right"
points.forEach((p, i) => {
if (
// The next point should have the same y coordinate
p.point.y === previousPointInPath.point.y &&
// The next point should have a larger x coordinate
p.point.x > previousPointInPath.point.x &&
// If the previous point is north, then the next point should be north as well. Similar for south
p.interCardinal[0] === previousPointInPath.interCardinal[0]
) {
if (nextPoint == null) {
nextPoint = p;
nextPointIndex = i;
return;
} else if (p.point.x < nextPoint.point.x) {
// This is closer to the previous point than the one we already found
nextPoint = p;
nextPointIndex = i;
return;
}
}
});
break;
case 'left':
points.forEach((p, i) => {
if (
p.point.y === previousPointInPath.point.y &&
p.point.x < previousPointInPath.point.x &&
p.interCardinal[0] === previousPointInPath.interCardinal[0]
) {
if (nextPoint == null) {
nextPoint = p;
nextPointIndex = i;
return;
} else if (p.point.x > nextPoint.point.x) {
nextPoint = p;
nextPointIndex = i;
return;
}
}
});
break;
case 'up':
points.forEach((p, i) => {
if (
p.point.x === previousPointInPath.point.x &&
p.point.y < previousPointInPath.point.y &&
p.interCardinal[1] === previousPointInPath.interCardinal[1]
) {
if (nextPoint == null) {
nextPoint = p;
nextPointIndex = i;
return;
} else if (p.point.y > nextPoint.point.y) {
nextPoint = p;
nextPointIndex = i;
return;
}
}
});
break;
case 'down':
points.forEach((p, i) => {
if (
p.point.x === previousPointInPath.point.x &&
p.point.y > previousPointInPath.point.y &&
p.interCardinal[1] === previousPointInPath.interCardinal[1]
) {
if (nextPoint == null) {
nextPoint = p;
nextPointIndex = i;
return;
} else if (p.point.y < nextPoint.point.y) {
nextPoint = p;
nextPointIndex = i;
return;
}
}
});
break;
}
// If we didn't find anything, we should close the loop
if (nextPoint == null || nextPointIndex == null) return undefined;
return [nextPoint, nextPointIndex];
}
// Find the oultine of cells in a grid.
function createOutlines(
positions: Position[],
dimensions: Dimensions
): OutlinePoint[][] {
const points = findOutlinePoints(positions, dimensions);
const paths: OutlinePoint[][] = [];
while (points.length > 0) {
// This loop creates new outlines until there are no points left
const pathPoints: OutlinePoint[] = [];
const [topLeftPoint, index] = findTopLeftOutlinePoint(points);
if (topLeftPoint == null) return [];
// Remove the top left point
points.splice(index, 1);
// And add it to the path
pathPoints.push(topLeftPoint);
let direction: Direction = 'up';
while (true) {
const previousPointInPath = pathPoints[pathPoints.length - 1];
direction = NextDirection[direction][previousPointInPath.interCardinal];
const nextPointInfo = findNextPoint(previousPointInPath, points, direction);
if (nextPointInfo == null) {
// We have reached the end
pathPoints.push(topLeftPoint); // Add the first point to the end to make a loop
paths.push(pathPoints);
break;
}
const [nextPoint, nextPointIndex] = nextPointInfo;
points.splice(nextPointIndex, 1);
pathPoints.push(nextPoint);
}
}
return paths;
}