Hexagonal tessellations, or tilings of the plane, are three-colorable -- meaning that using only three colors, such as Red, Blue, and Green, every hexagon can be colored without any two hexagons that share a border sharing a color. A partial example showing such a pattern.

Given a partial hexagonal tiling in ASCII (via STDIN, reading from a file, etc.), change the characters' colors (via ANSI codes, image manipulation, etc.) to fit this coloring. The twist is the middle of the hexagons (the *, below) are the three colors, and the lines between need to be a mix of their two corresponding hex's * colors. If the line is on the outside border, it should match the corresponding *.

For example, given the partial hexagonal tiling of

 / \ / \ / \ / \
| * | * | * | * |
 \ / \ / \ / \ /
  | * | * | * |
   \ / \ / \ /

suppose we decide to color the top-left hex * to be Red, and its two neighbors to be Blue and Green (going clockwise). Then the | line needs to be Magenta and the / line needs to be Yellow. If we keep coloring, we'll eventually end up with something like this (enlarged for clarity):

Hexagonal Coloring 1

Or, for an input of

     / \
    | * |
   / \ /
  | * |
 / \ /
| * |
 \ /

you might color it like so (enlarged for clarity):

Hexagonal Coloring 2

A few additional test cases (your code should be able to handle these):

 / \ / \
| * | * |
 \ / \ /
  | * |
 / \ / \
| * | * |
 \ / \ /


 / \
| * |
 \ / \
  | * |
 / \ /
| * |
 \ /



 / \
| * |
 \ /

Rules

  • The input is guaranteed to have at least one hexagon, and no input will have a "hole."
  • You don't need to start your coloring with Red, so long as you maintain the three-color rule.
  • If the partial tiling can be two-colored, you can do so without penalty (such as in the second example) - you don't necessarily need to extrapolate the partial tiling to a full tiling.
  • The hexagon centers * must be colored with either Red, Blue, or Green, while the lines between must be either Cyan, Yellow, or Magenta. For example, having a Magenta * is not allowed, and a Red | or \ or / must be on the outside border of the drawing. See Colors, below.
  • If your console doesn't have these exact colors, please use the closest approximation and specify in your answer what approximation you're using.
  • Leading or trailing whitespace, including trailing newlines, are acceptable so long as the characters line up.
  • The partial tiling can be input with space-padding to form a rectangle, if that makes it easier for your code.
  • Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
  • Output can be to the console, saved as an image, etc.
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

Colors and Color Mixtures:

The three available base colors are (with color codes in RGB decimal format):

  • Red (255,0,0)
  • Green (0,255,0)
  • Blue (0,0,255)

The combinations are:

  • Red and Green combine to make Yellow (255,255,0)
  • Blue and Green combine to make Cyan (0,255,255)
  • Red and Blue combine to make Magenta (255,0,255)
  • Can you add a test case with a hole please? Like the first (additional) one, but without the middle * – H.PWiz Nov 3 '17 at 13:34
  • If extra whitespace is given at the end of lines or newlines at the end of the input, do they have to be preserved? – HyperNeutrino Nov 3 '17 at 13:35
  • @H.PWiz Such a situation would never arise. No need to worry about it. – AdmBorkBork Nov 3 '17 at 13:41
  • @HyperNeutrino No, that's purely your decision. Input/output whitespace formatting isn't the interesting part of this challenge. – AdmBorkBork Nov 3 '17 at 13:42
  • Okay. Thanks. Also, will we ever have to handle empty input? – HyperNeutrino Nov 3 '17 at 13:46

JavaScript (ES6), 219 203 201 bytes

f=
s=>s.split`
`.map((b,i,a)=>b.replace(/./g,(c,j)=>`<font color=#${g=(x,y)=>((a[x+i]||``)[b=y+j]==`*`&&15<<b%3*4)|(y<0?8<<9:g(x,y-2)),(g(0,2)|g(-1,1)|g(1,1)).toString(16).slice(1)}>${c}</font>`)).join`
`
<input type=button value="Colourise!" onclick=i.innerHTML=f(i.textContent)>
<pre id=i contenteditable> / \ / \
| * | * |
 \ / \ / \
  | * | * |
 / \ / \ / \
| * |   | * |
 \ / \ / \ / \
  | * | * | * |
 / \ / \ / \ /
| * | * |
 \ / \ /</pre>

Explanation: Each character is wrapped in a font tag to set its colour, which is calculated by checking each square, plus the squares two to the left and right, plus the four squares one away diagonally, for *s and if so combining the colours of all *s found. The * colours are chosen simply by taking their horizontal coordinate modulo 3 and shifting a bitmask appropriately. Edit: Saved 2 bytes by switching from #RRGGBB to #RGB colours.

  • You don't need to handle inner holes, if that makes a difference. – AdmBorkBork Nov 3 '17 at 16:46
  • 2
    @AdmBorkBork No, I was just showing off. – Neil Nov 3 '17 at 17:13
  • "Colourise!" - You spelled this wrong. – OldBunny2800 Nov 3 '17 at 21:14
  • 3
    @OldBunny2800 It's spelt correctly in my language... – Neil Nov 3 '17 at 21:52
  • 2
    @OldBunny2800 American isn't the only spelling-set that exists. There's British (a superset of English English), Australian (which for some reason includes New Zealand - perhaps it should be called Australasian) and Canadian (that place north of the majority of the USA), for instance. – wizzwizz4 Nov 3 '17 at 21:52

JavaScript (ES6), 210 bytes (using HTML + CSS)

Similar to my canvas approach; locates all the *s in the input string and writes hexagons to the page in the form of absolutely positioned <pre> elements. Because mix-blend-mode is set to lighten, color addition is performed automatically when characters overlap.

s=>s.split`
`.map((r,y)=>[...r].map((c,x)=>c=='*'&&document.write(`<pre style=position:fixed;mix-blend-mode:lighten;line-height:1;left:${x}ch;top:${y}em;color:${['red','lime','blue'][x%3]}> / \\
| * |
 \\ /`)))

f=

s=>s.split`
`.map((r,y)=>[...r].map((c,x)=>c=='*'&&document.write(`<pre style=position:fixed;mix-blend-mode:lighten;line-height:1;left:${x}ch;top:${y}em;color:${['red','lime','blue'][x%3]}> / \\
| * |
 \\ /`)))

f(` / \\ / \\ / \\ / \\
| * | * | * | * |
 \\ / \\ / \\ / \\ /
  | * | * | * |
   \\ / \\ / \\ /`)

Python 2, 279 bytes

e=enumerate
x=input()
c=[[i,j]for i,t in e(x)for j,_ in e(t)if"*"==x[i][j]]
C=[[j%3*([i,j]in c)for j,_ in e(o)]for i,o in e(x)]
for J,K in c:
	for i in-1,0,1:q=2-(i&1);_=C[i+J];_[K+q]=_[K-q]=_[K-q]|C[J][K]
for i,o in e(x):print"".join("[%dm"%(30+C[i][j])+x[i][j]for j,_ in e(o))

Try it online!

Golfed and fixed thanks to user202729!
-27 bytes thanks to Mr. Xcoder
-24 bytes thanks to Jonathan Frech

  • 337 bytes. – Mr. Xcoder Nov 3 '17 at 16:42
  • Also 2-abs(i) is 2-(i&1) in this context, 336 bytes. – Mr. Xcoder Nov 3 '17 at 16:48
  • @Mr.Xcoder Cool, thanks! – HyperNeutrino Nov 3 '17 at 17:03
  • I think \033 can be \33 (saving one byte) or the actual \x1b character (saving three bytes). – Jonathan Frech Nov 3 '17 at 18:32
  • 1
    @OldBunny2800 I think that's the same length. You swap two = and a ; for two , and a =. – wizzwizz4 Nov 3 '17 at 21:50

Python 2, 346 331 bytes

e=enumerate
C='0132645'
def f(s):
 c={(i,j):[1+(i/2%2+j/4)%3*2]for i,l in e(s)for j,x in e(l)if'*'==x}
 for i,l in e(s):
  r=''
  for j,x in e(l):a=c.get((i,j),c.get((i-(x<'|'),j+[-1,1][x>'/']+(x>'z')),[]))+c.get((i+(x<'|'),j+[1,-1][x>'/']-(x>'z')),[])if' '<x else[0];r+='\033[3'+C[[sum(a)/len(a),6][set(a)=={5,1}]]+'m'+x
  print r

Try it online!

HTML (Canvas) + JavaScript (ES6), 13 + 251 = 264 bytes

Locates all the *s in the input string and paints an ASCII hexagon on the canvas at the corresponding positions. Because globalCompositeOperation='lighter', color addition is performed automatically when characters overlap.

HTML

<canvas id=c>

JavaScript

s=>{c.width=c.height=s.length
with(c.getContext`2d`)font='1px monospace',globalCompositeOperation='lighter',s.split`
`.map((r,y)=>[...r].map((c,x)=>c=='*'&&[` / \\`,`| * |`,` \\ /`].map((t,i)=>fillText(t,x,y+i),fillStyle=['red','lime','blue'][x%3])))}

A multiplier and additional scale() command were added to the snippet for visibility.

// Unscaled
/*
f=
s=>{c.width=c.height=s.length
with(c.getContext`2d`)font='1px monospace',globalCompositeOperation='lighter',s.split`
`.map((r,y)=>[...r].map((c,x)=>c=='*'&&[` / \\`,`| * |`,` \\ /`].map((t,i)=>fillText(t,x,y+i),fillStyle=['red','lime','blue'][x%3])))}
*/


// Scaled
let scaleFactor = 40

f=
s=>{c.width=c.height=s.length*scaleFactor
with(c.getContext`2d`)scale(scaleFactor,scaleFactor),font='1px monospace',globalCompositeOperation='lighter',s.split`
`.map((r,y)=>[...r].map((c,x)=>c=='*'&&[` / \\`,`| * |`,` \\ /`].map((t,i)=>fillText(t,x,y+i),fillStyle=['red','lime','blue'][x%3])))}


// Test
f(` / \\ / \\ / \\ / \\
| * | * | * | * |
 \\ / \\ / \\ / \\ /
  | * | * | * |
   \\ / \\ / \\ /`)
<canvas id=c>

enter image description here


Also see my CSS-based approach.

  • I don't believe this meets the required output: https://i.stack.imgur.com/Pp3J0.png. The forward and backslashes are supposed to be the additive combinations of the colors of the hexagons they border, not separate. – Patrick Roberts Nov 4 '17 at 3:36
  • @PatrickRoberts Hmm, looks to be an issue with inconsistent font widths (the positions are correct on my computer). I'll look into this later today. – darrylyeo Nov 4 '17 at 15:37
  • Oh, I didn't realize there was a platform that was correct. If you update your answer with a screenshot, I'll revert my vote. Answers only need to work on at least one implementation, they don't need to be cross-platform. – Patrick Roberts Nov 4 '17 at 15:57

MATLAB/Octave, 223 bytes

a=input('')';s=size(a);p=zeros([s 3]);[i,j]=ind2sub(s,find(a=='*'));
for f=1:nnz(i)
p((-2:2)+i(f),(-1:1)+j(f),mod(f+~mod(j(f)/2,2),3)+1)=1;end
for f=1:nnz(a)
[i,j]=ind2sub(s,f);text(i/s(1),1-j/s(2),a(i,j),'Co',p(i,j,:));end

In slightly neater code format:

a=input('')';                  %Grab input as 2D array
s=size(a);                     %Get input size
p=zeros([s 3]);                %Make empty colour matrix of matching size with RGB
[i,j]=ind2sub(s,find(a=='*')); %Find all *'s
for f=1:nnz(i)                 %For each *
    %Fill a 5x3 box centred at the * on the colour channel for this box
    %Overlapping regions between boxes will result in the correct mix.
    p((-2:2)+i(f),(-1:1)+j(f),mod(f+~mod(j(f)/2,2),3)+1)=1;
end
%Display as text on a figure
for f=1:nnz(a)
    [i,j]=ind2sub(s,f);
    text(i/s(1),1-j/s(2),a(i,j),'Co',p(i,j,:))
end

The input is taken as a 2D array, such as entering the following when prompted for input:

[' / \ / \ / \ / \ ';'| * | * | * | * |';' \ / \ / \ / \ / ';'  | * | * | * |  ';'   \ / \ / \ /   ']

MATLAB so far as I'm aware doesn't have the ability to output colours to the console (except for dirty Java hacks which I am discounting). As such the output is instead printed to a figure.

Colouring is achieved by finding the location of all *'s in the input, and then in an RGB colour array (p), a 5x3 box of 1's (255 in MATLAB colour representation) are written centred about the *. The box is written to the colour corresponding with the mod-3 index along each line, with even lines having the colour index shifted by an offset.

This produces a colour matrix where any boxes that overlap will result in the required mixed colour. The example above produces the following colour matrix.

Colour Matrix Example

The white and black regions are irrelevant because at those locations a space is printed meaning we don't actually see the incorrect colour.

Once the colour matrix is created, we display each character on a figure using the text command, setting the colour of the text to the corresponding entry in the colour matrix. The above example will display:

Sample Output

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.