#!/usr/bin/env python # -*- coding: utf-8 -*- # This file is part of A Robot's Conundrum. # # A Robot's Conundrum is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # A Robot's Conundrum is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # A Robot's Conundrum. If not, see . # # ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' # # colourboxes.py # -------------------- # date created : Wed Aug 8 2012 # copyright : (C) 2012 Niels G. W. Serup # maintained by : Niels G. W. Serup """ Colour boxes. """ from __future__ import print_function import random import itertools def generate_colour_boxes(nwells, nboxes): """ Generate colour boxes that can be used to make all wells white. None of the generated colour boxes are white. Arguments: nwells -- number of wells nboxes -- maximum number of boxes needed to make all wells white. Return [[(r, g, b)]] where r : 0|1, g : 0|1, b : 0|1 """ nbits = nwells * 3 data = [[0 for _ in range(nboxes)] for _ in range(nbits)] def insert_1(x): t = random.randrange(0, nboxes) x0, x1 = _get_oxs(x) for y in range(t, nboxes) + range(0, t): if data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1): data[x][y] = 1 break else: raise Exception("Cannot maintain no 111s invariant.") def insert_two_1(x): t = random.randrange(0, nboxes) x0, x1 = _get_oxs(x) if len(list(filter(lambda y: data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1), range(nboxes)))) < 2: return for _ in range(2): for y in range(t, nboxes) + range(0, t): if data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1): data[x][y] = 1 break for x in range(len(data)): insert_1(x) for x in range(len(data)): for _ in range(random.randrange(0, (nboxes + 1) / 2)): insert_two_1(x) boxes = [] for y in range(nboxes): box = [] boxes.append(box) for x in range(0, nbits, 3): r = data[x][y] g = data[x + 1][y] b = data[x + 2][y] box.append((r, g, b)) return boxes def _get_oxs(x): return (x + 1, x + 2) if x % 3 == 0 \ else (x - 1, x + 1) if x % 3 == 1 \ else (x - 2, x - 1) def generate_random_box(nwells, min_nonblacks=0): """ Generate a box that triggers nwells wells, with random colors except white (111). Arguments: min_nonblacks -- minimum number of well colours in a box required not to be black. """ def gen_wc(): wc = [random.choice((0, 1)) for i in range(3)] if all(b == 1 for b in wc): wc[random.randrange(3)] = 0 return wc def gen_wc_nonblack(): wc = gen_wc() if all(b == 0 for b in wc): wc[random.randrange(3)] = 1 return wc colours = [tuple(gen_wc()) for _ in range(nwells)] nonblack = lambda t: any(n == 1 for n in t) missing_nonblacks = min_nonblacks - len(list(filter(nonblack, colours))) i = 0 while missing_nonblacks > 0: if not nonblack(colours[i]): colours[i] = gen_wc_nonblack() missing_nonblacks -= 1 i += 1 return colours def get_colours(boxes): colours = [] for i in range(len(boxes[0])): r, g, b = boxes[0][i] for j in range(1, len(boxes)): r1, g1, b1 = boxes[j][i] r ^= r1 g ^= g1 b ^= b1 colours.append((r, g, b)) return colours def makes_all_wells_white(boxes): """ Determine if the boxes make all wells white when XOR'ed together. """ return all(c == 1 for c in itertools.chain(*get_colours(boxes)))