Task: Railway circuit




Go: package main

import "fmt"

const (
    right    = 1
    left     = -1
    straight = 0
)

func normalize(tracks []int) string {
    size := len(tracks)
    a := make([]byte, size)
    for i := 0; i < size; i++ {
        a[i] = "abc"[tracks[i]+1]
    }

    

    norm := string(a)
    for i := 0; i < size; i++ {
        s := string(a)
        if s < norm {
            norm = s
        }
        tmp := a[0]
        copy(a, a[1:])
        a[size-1] = tmp
    }
    return norm
}

func fullCircleStraight(tracks []int, nStraight int) bool {
    if nStraight == 0 {
        return true
    }

    
    count := 0
    for _, track := range tracks {
        if track == straight {
            count++
        }
    }
    if count != nStraight {
        return false
    }

    
    var straightTracks [12]int
    for i, idx := 0, 0; i < len(tracks) && idx >= 0; i++ {
        if tracks[i] == straight {
            straightTracks[idx%12]++
        }
        idx += tracks[i]
    }
    any1, any2 := false, false
    for i := 0; i <= 5; i++ {
        if straightTracks[i] != straightTracks[i+6] {
            any1 = true
            break
        }
    }
    for i := 0; i <= 7; i++ {
        if straightTracks[i] != straightTracks[i+4] {
            any2 = true
            break
        }
    }
    return !any1 || !any2
}

func fullCircleRight(tracks []int) bool {
    
    sum := 0
    for _, track := range tracks {
        sum += track * 30
    }
    if sum%360 != 0 {
        return false
    }

    
    var rTurns [12]int
    for i, idx := 0, 0; i < len(tracks) && idx >= 0; i++ {
        if tracks[i] == right {
            rTurns[idx%12]++
        }
        idx += tracks[i]
    }
    any1, any2 := false, false
    for i := 0; i <= 5; i++ {
        if rTurns[i] != rTurns[i+6] {
            any1 = true
            break
        }
    }
    for i := 0; i <= 7; i++ {
        if rTurns[i] != rTurns[i+4] {
            any2 = true
            break
        }
    }
    return !any1 || !any2
}

func circuits(nCurved, nStraight int) {
    solutions := make(map[string][]int)
    gen := getPermutationsGen(nCurved, nStraight)
    for gen.hasNext() {
        tracks := gen.next()
        if !fullCircleStraight(tracks, nStraight) {
            continue
        }
        if !fullCircleRight(tracks) {
            continue
        }
        tracks2 := make([]int, len(tracks))
        copy(tracks2, tracks)
        solutions[normalize(tracks)] = tracks2
    }
    report(solutions, nCurved, nStraight)
}

func getPermutationsGen(nCurved, nStraight int) PermutationsGen {
    if (nCurved+nStraight-12)%4 != 0 {
        panic("input must be 12 + k * 4")
    }
    var trackTypes []int
    switch nStraight {
    case 0:
        trackTypes = []int{right, left}
    case 12:
        trackTypes = []int{right, straight}
    default:
        trackTypes = []int{right, left, straight}
    }
    return NewPermutationsGen(nCurved+nStraight, trackTypes)
}

func report(sol map[string][]int, numC, numS int) {
    size := len(sol)
    fmt.Printf("\n%d solution(s) for C%d,%d \n", size, numC, numS)
    if numC <= 20 {
        for _, tracks := range sol {
            for _, track := range tracks {
                fmt.Printf("%2d ", track)
            }
            fmt.Println()
        }
    }
}


type PermutationsGen struct {
    NumPositions int
    choices      []int
    indices      []int
    sequence     []int
    carry        int
}

func NewPermutationsGen(numPositions int, choices []int) PermutationsGen {
    indices := make([]int, numPositions)
    sequence := make([]int, numPositions)
    carry := 0
    return PermutationsGen{numPositions, choices, indices, sequence, carry}
}

func (p *PermutationsGen) next() []int {
    p.carry = 1

    
    for i := 1; i < len(p.indices) && p.carry > 0; i++ {
        p.indices[i] += p.carry
        p.carry = 0
        if p.indices[i] == len(p.choices) {
            p.carry = 1
            p.indices[i] = 0
        }
    }
    for j := 0; j < len(p.indices); j++ {
        p.sequence[j] = p.choices[p.indices[j]]
    }
    return p.sequence
}

func (p *PermutationsGen) hasNext() bool {
    return p.carry != 1
}

func main() {
    for n := 12; n <= 28; n += 4 {
        circuits(n, 0)
    }
    circuits(12, 4)
}


Java: package railwaycircuit;

import static java.util.Arrays.stream;
import java.util.*;
import static java.util.stream.IntStream.range;

public class RailwayCircuit {
    final static int RIGHT = 1, LEFT = -1, STRAIGHT = 0;

    static String normalize(int[] tracks) {
        char[] a = new char[tracks.length];
        for (int i = 0; i < a.length; i++)
            a[i] = "abc".charAt(tracks[i] + 1);

        
        String norm = new String(a);
        for (int i = 0, len = a.length; i < len; i++) {

            String s = new String(a);
            if (s.compareTo(norm) < 0)
                norm = s;

            char tmp = a[0];
            for (int j = 1; j < a.length; j++)
                a[j - 1] = a[j];
            a[len - 1] = tmp;
        }
        return norm;
    }

    static boolean fullCircleStraight(int[] tracks, int nStraight) {
        if (nStraight == 0)
            return true;

        
        if (stream(tracks).filter(i -> i == STRAIGHT).count() != nStraight)
            return false;

        
        int[] straight = new int[12];
        for (int i = 0, idx = 0; i < tracks.length && idx >= 0; i++) {
            if (tracks[i] == STRAIGHT)
                straight[idx % 12]++;
            idx += tracks[i];
        }

        return !(range(0, 6).anyMatch(i -> straight[i] != straight[i + 6])
                && range(0, 8).anyMatch(i -> straight[i] != straight[i + 4]));
    }

    static boolean fullCircleRight(int[] tracks) {

        
        if (stream(tracks).map(i -> i * 30).sum() % 360 != 0)
            return false;

        
        int[] rTurns = new int[12];
        for (int i = 0, idx = 0; i < tracks.length && idx >= 0; i++) {
            if (tracks[i] == RIGHT)
                rTurns[idx % 12]++;
            idx += tracks[i];
        }

        return !(range(0, 6).anyMatch(i -> rTurns[i] != rTurns[i + 6])
                && range(0, 8).anyMatch(i -> rTurns[i] != rTurns[i + 4]));
    }

    static void circuits(int nCurved, int nStraight) {
        Map<String, int[]> solutions = new HashMap<>();

        PermutationsGen gen = getPermutationsGen(nCurved, nStraight);
        while (gen.hasNext()) {

            int[] tracks = gen.next();

            if (!fullCircleStraight(tracks, nStraight))
                continue;

            if (!fullCircleRight(tracks))
                continue;

            solutions.put(normalize(tracks), tracks.clone());
        }
        report(solutions, nCurved, nStraight);
    }

    static PermutationsGen getPermutationsGen(int nCurved, int nStraight) {
        assert (nCurved + nStraight - 12) % 4 == 0 : "input must be 12 + k * 4";

        int[] trackTypes = new int[]{RIGHT, LEFT};

        if (nStraight != 0) {
            if (nCurved == 12)
                trackTypes = new int[]{RIGHT, STRAIGHT};
            else
                trackTypes = new int[]{RIGHT, LEFT, STRAIGHT};
        }

        return new PermutationsGen(nCurved + nStraight, trackTypes);
    }

    static void report(Map<String, int[]> sol, int numC, int numS) {

        int size = sol.size();
        System.out.printf("%n%d solution(s) for C%d,%d %n", size, numC, numS);

        if (size < 10)
            sol.values().stream().forEach(tracks -> {
                stream(tracks).forEach(i -> System.out.printf("%2d ", i));
                System.out.println();
            });
    }

    public static void main(String[] args) {
        circuits(12, 0);
        circuits(16, 0);
        circuits(20, 0);
        circuits(24, 0);
        circuits(12, 4);
    }
}

class PermutationsGen {
    
    private int[] indices;
    private int[] choices;
    private int[] sequence;
    private int carry;

    PermutationsGen(int numPositions, int[] choices) {
        indices = new int[numPositions];
        sequence = new int[numPositions];
        this.choices = choices;
    }

    int[] next() {
        carry = 1;
        
        for (int i = 1; i < indices.length && carry > 0; i++) {
            indices[i] += carry;
            carry = 0;

            if (indices[i] == choices.length) {
                carry = 1;
                indices[i] = 0;
            }
        }

        for (int i = 0; i < indices.length; i++)
            sequence[i] = choices[indices[i]];

        return sequence;
    }

    boolean hasNext() {
        return carry != 1;
    }
}