How to code colors in hex

15

1

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)

AdmBorkBork

Posted 2017-11-03T13:14:38.990

Reputation: 41 581

Can you add a test case with a hole please? Like the first (additional) one, but without the middle * – H.PWiz – 2017-11-03T13:34:30.287

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 – 2017-11-03T13:35:30.387

@H.PWiz Such a situation would never arise. No need to worry about it. – AdmBorkBork – 2017-11-03T13:41:59.980

@HyperNeutrino No, that's purely your decision. Input/output whitespace formatting isn't the interesting part of this challenge. – AdmBorkBork – 2017-11-03T13:42:28.570

Okay. Thanks. Also, will we ever have to handle empty input? – HyperNeutrino – 2017-11-03T13:46:05.407

@HyperNeutrino Nope, you're guaranteed at least one hex in the input. I'll clarify. – AdmBorkBork – 2017-11-03T13:51:02.843

Answers

7

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.

Neil

Posted 2017-11-03T13:14:38.990

Reputation: 95 035

You don't need to handle inner holes, if that makes a difference. – AdmBorkBork – 2017-11-03T16:46:25.897

2@AdmBorkBork No, I was just showing off. – Neil – 2017-11-03T17:13:36.260

"Colourise!" - You spelled this wrong. – OldBunny2800 – 2017-11-03T21:14:48.680

3@OldBunny2800 It's spelt correctly in my language... – Neil – 2017-11-03T21:52:23.427

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 – 2017-11-03T21:52:55.807

@wizzwizz4 I'm dying. Thank you for that laugh. – rayryeng - Reinstate Monica – 2017-11-04T00:03:34.167

I think you can remove the # from the colour specification to save a byte. You might also be able to (ab)use html's high tolerance for colour values to save some more bytes... – Luke – 2017-11-04T01:02:34.233

@Luke Removing the # doesn't seem to work, but you inspired me to switch to #RGB colour values to save two bytes. – Neil – 2017-11-04T10:37:43.080

4

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(` / \\ / \\ / \\ / \\
| * | * | * | * |
 \\ / \\ / \\ / \\ /
  | * | * | * |
   \\ / \\ / \\ /`)

darrylyeo

Posted 2017-11-03T13:14:38.990

Reputation: 6 214

3

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

HyperNeutrino

Posted 2017-11-03T13:14:38.990

Reputation: 26 575

337 bytes. – Mr. Xcoder – 2017-11-03T16:42:29.753

Also 2-abs(i) is 2-(i&1) in this context, 336 bytes. – Mr. Xcoder – 2017-11-03T16:48:26.443

@Mr.Xcoder Cool, thanks! – HyperNeutrino – 2017-11-03T17:03:29.403

I think \033 can be \33 (saving one byte) or the actual \x1b character (saving three bytes).

– Jonathan Frech – 2017-11-03T18:32:02.077

298 bytes. – Jonathan Frech – 2017-11-03T18:35:03.663

@JonathanFrech oh cool, thanks! :D – HyperNeutrino – 2017-11-03T20:33:15.917

Line 1: e,x=enumerate,input() – OldBunny2800 – 2017-11-03T21:13:48.357

1@OldBunny2800 I think that's the same length. You swap two = and a ; for two , and a =. – wizzwizz4 – 2017-11-03T21:50:26.197

Yeah, you're right. – OldBunny2800 – 2017-11-03T21:53:47.663

2

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!

TFeld

Posted 2017-11-03T13:14:38.990

Reputation: 19 246

1

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.

darrylyeo

Posted 2017-11-03T13:14:38.990

Reputation: 6 214

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 – 2017-11-04T03:36:22.437

@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 – 2017-11-04T15:37:12.090

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 – 2017-11-04T15:57:26.567

1

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

Tom Carpenter

Posted 2017-11-03T13:14:38.990

Reputation: 3 990