13
\$\begingroup\$

The Four color theorem States that no more than four colors are required to color the regions of a map.

The challenge

Given a list of State borders assign each state ID a color so that no two adjacent states have the same color. The output Should be a CSS stylesheet assigning the color to the state's 2 letter ID code. Here is a SVG map which the stylesheet could be applied to. http://upload.wikimedia.org/wikipedia/commons/3/32/Blank_US_Map.svg

The rules

  • Shortest code wins
  • any state border list can be used
  • only 4 colors can be used.
  • the state list can be hardcoded

Advice: Use the CSS fill: property to change the color, For example #AL{fill:green}

Here is a list of state borders

AL-FL
AL-GA
AL-MS
AL-TN
AR-LA
AR-MO
AR-MS
AR-OK
AR-TN
AR-TX
AZ-CA
AZ-CO
AZ-NM
AZ-NV
AZ-UT
CA-NV
CA-OR
CO-KS
CO-NE
CO-NM
CO-OK
CO-UT
CO-WY
CT-MA
CT-NY
CT-RI
DC-MD
DC-VA
DE-MD
DE-NJ
DE-PA
FL-GA
GA-NC
GA-SC
GA-TN
IA-MN
IA-MO
IA-NE
IA-SD
IA-WI
ID-MT
ID-NV
ID-OR
ID-UT
ID-WA
ID-WY
IL-IA
IL-IN
IL-KY
IL-MO
IL-WI
IN-KY
IN-MI
IN-OH
KS-MO
KS-NE
KS-OK
KY-MO
KY-OH
KY-TN
KY-VA
KY-WV
LA-MS
LA-TX
MA-NH
MA-NY
MA-RI
MA-VT
MD-PA
MD-VA
MD-WV
ME-NH
MI-OH
MI-WI
MN-ND
MN-SD
MN-WI
MO-NE
MO-OK
MO-TN
MS-TN
MT-ND
MT-SD
MT-WY
NC-SC
NC-TN
NC-VA
ND-SD
NE-SD
NE-WY
NH-VT
NJ-NY
NJ-PA
NM-OK
NM-TX
NM-UT
NV-OR
NV-UT
NY-PA
NY-VT
OH-PA
OH-WV
OK-TX
OR-WA
PA-WV
SD-WY
TN-VA
UT-WY
VA-WV
\$\endgroup\$
9
  • \$\begingroup\$ Can we hardcode the list of state borders? \$\endgroup\$ Oct 25, 2014 at 22:59
  • \$\begingroup\$ @hsl yes, it is ok to hardcode state borders. \$\endgroup\$
    – kyle k
    Oct 26, 2014 at 3:26
  • \$\begingroup\$ @steveverrill if you can think of a better method of changing colors that would be great. I added an example showing how to use CSS. \$\endgroup\$
    – kyle k
    Oct 26, 2014 at 3:37
  • \$\begingroup\$ Wouldn't this require reproducing the proof the Four Color Theorem itself? Since you have to handle every possible case? \$\endgroup\$
    – user503
    Oct 26, 2014 at 5:19
  • 1
    \$\begingroup\$ Wouldn't this Theorem prove wrong if a state's border is touching more than 3 other states ? \$\endgroup\$
    – Optimizer
    Oct 26, 2014 at 6:11

3 Answers 3

4
\$\begingroup\$

Python, 320 chars

import sys,random
S=[]
E={}
for x in sys.stdin:a=x[:2];b=x[3:5];S+=[a,b];E[a,b]=E[b,a]=1
C={0:0}
while any(1>C[s]for s in C):
 C={s:0for s in S};random.shuffle(S)
 for s in S:
    A=set([1,2,3,4])-set(C[y]for x,y in E if x==s)
    if A:C[s]=random.choice(list(A))
for s in C:print'#%s{fill:%s}'%(s,' bglrloieulmdede'[C[s]::4])

Uses a randomized algorithm. Assign colors to states in random order by selecting a color that doesn't conflict with adjacent states that have already been colored. Seems to work in a tenth of a second or so on the given input.

Example output:

$ 4color.py < stategraph
#WA{fill:red}
#DE{fill:gold}
#DC{fill:blue}
#WI{fill:blue}
#WV{fill:red}
#FL{fill:lime}
#WY{fill:gold}
#NH{fill:red}
#NJ{fill:lime}
#NM{fill:gold}
#TX{fill:red}
#LA{fill:blue}
#NC{fill:blue}
#ND{fill:gold}
#NE{fill:blue}
#TN{fill:red}
#NY{fill:gold}
#PA{fill:blue}
#RI{fill:gold}
#NV{fill:red}
#VA{fill:gold}
#CO{fill:red}
#CA{fill:gold}
#AL{fill:blue}
#AR{fill:gold}
#VT{fill:lime}
#IL{fill:red}
#GA{fill:gold}
#IN{fill:lime}
#IA{fill:gold}
#OK{fill:blue}
#AZ{fill:lime}
#ID{fill:lime}
#CT{fill:red}
#ME{fill:blue}
#MD{fill:lime}
#MA{fill:blue}
#OH{fill:gold}
#UT{fill:blue}
#MO{fill:lime}
#MN{fill:red}
#MI{fill:red}
#KS{fill:gold}
#MT{fill:blue}
#MS{fill:lime}
#SC{fill:red}
#KY{fill:blue}
#OR{fill:blue}
#SD{fill:lime}

Example pasted into svg.

\$\endgroup\$
5
  • \$\begingroup\$ tan is apparently a supported SVG colour. Shame that you can only get one three-colour one with the ::4 trick. \$\endgroup\$ Oct 26, 2014 at 8:00
  • 1
    \$\begingroup\$ @PeterTaylor: tan looks awful. Totally worth 1 character to use gold instead. \$\endgroup\$ Oct 26, 2014 at 8:02
  • \$\begingroup\$ Can you guarantee this algorithm will always finish in finite time provided a 4-color solution exists? :) \$\endgroup\$
    – user503
    Oct 26, 2014 at 17:18
  • \$\begingroup\$ @barrycarter: It is guaranteed to finish with probability 1. It might take time exponential in the size of the map, though. \$\endgroup\$ Oct 26, 2014 at 17:42
  • \$\begingroup\$ @KeithRandall I was sort of teasing, but... if you check for repeats, it could take 4^(n-1) steps to find the right coloring (n-1 because of symmetry of the colors). If you don't check for repeats, it could take longer still. I just found the solution unsatisfying, since it's not "really" a "proper" algorithm. \$\endgroup\$
    – user503
    Oct 26, 2014 at 17:57
3
\$\begingroup\$

Prolog, 309 307 283 chars

:-initialization m.
a-X:-assert(X);retract(X),1=0.
r:-maplist(get_char,[A,B,E,C,D,F]),(E=F;X=[A,B],Y=[C,D],a-X/Y,a-Y/X,(s/X;a-s/X),(s/Y;a-s/Y),r).
s+[]:- \+ (X*C,writef('#%s{fill:#%w}',[X,C]),1=0).
s+[X|T]:-member(C,[911,191,119,991]),a-X*C,\+ (X/Y,Y*C),s+T.
m:-r,bagof(X,s/X,L),s+L.

The algorithm uses backtracking / depth-first search to fill out the map.

A bit more readable:

:- initialization(main).

% Found on http://awarth.blogspot.de/2008/08/asserts-and-retracts-with-automatic.html
assert2(X) :- assert(X).
assert2(X) :- retract(X), fail.

% Reads all states into clauses "state-State",
% and all connections into "State-Neighbor" and "Neighbor-State".
read_states :-
    % Read a line "AB-CD\n"
    maplist(get_char, [A,B,E,C,D,F]),
    (   A = F;
        State = [A, B],
        Neighbor = [C, D],
        % Memorize the connection between State and Neighbor in both directions.
        assert(State/Neighbor),
        assert(Neighbor/State),
        % Memorize State and Neighbor for the list of states.
        (state/State; assert(state/State)),
        (state/Neighbor; assert(state/Neighbor)),
        % Continue for all lines.
        read_states
    ).

% Print out all colors.
solve([]) :-
    once((
        State*Color,
        writef('#%s{fill:%w}', [State, Color]),
        fail
    )); !.

% Use depth-first search to color the map.
solve([State|FurtherStates]) :-
    member(Color, ['#911', '#191', '#119', '#991']),
    assert2(State*Color),
    \+ (State/Neighbor, Neighbor*Color),
    solve(FurtherStates).

main :-
    read_states,
    bagof(State, state/State, States),
    solve(States).

Invocation:

cat borders.txt | swipl -q ./fourcolors.pl

Result (newlines are not needed):

#AL{fill:#911}#FL{fill:#191}#GA{fill:#119}#MS{fill:#191}#TN{fill:#991}#AR{fill:#911}#LA{fill:#119}#MO{fill:#191}#OK{fill:#119}#TX{fill:#191}#AZ{fill:#911}#CA{fill:#191}#CO{fill:#191}#NM{fill:#991}#NV{fill:#991}#UT{fill:#119}#OR{fill:#911}#KS{fill:#911}#NE{fill:#119}#WY{fill:#911}#CT{fill:#911}#MA{fill:#191}#NY{fill:#119}#RI{fill:#119}#DC{fill:#911}#MD{fill:#191}#VA{fill:#119}#DE{fill:#119}#NJ{fill:#191}#PA{fill:#911}#NC{fill:#911}#SC{fill:#191}#IA{fill:#911}#MN{fill:#191}#SD{fill:#991}#WI{fill:#119}#ID{fill:#191}#MT{fill:#119}#WA{fill:#119}#IL{fill:#991}#IN{fill:#191}#KY{fill:#911}#MI{fill:#911}#OH{fill:#119}#WV{fill:#991}#NH{fill:#911}#VT{fill:#991}#ME{fill:#191}#ND{fill:#911}

Pasted into an SVG: http://jsbin.com/toniseqaqi/

\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6) 269 279

Recursive search with backtracking. ~80 bytes spent for state list parsing.

 F=l=>{
   S=(a,b)=>S[a]=(S[a]||[]).concat(b),
   l.replace(/(..)-(..)/g,(_,a,b)=>S(a,b)+S(b,a)),
   k=Object.keys(S),
   R=(p,c=k[p])=>!c||['blue','gold','red','tan'].some(i=>!c.some(t=>S[t].c==i)&&(c.c=i,R(p+1)||(c.c='')),c=S[c]),
   R(0),
   k.map(k=>console.log('#'+k+'{fill:'+S[k].c+'}'))
 }

Ungolfed

F=l=>{
  var states = {}; // hash table with adiacent list for each state
  S=(a,b)=>states[a]=(states[a]||[]).concat(b);
  l.replace(/(..)-(..)/g,(_,a,b)=>S(a,b)+S(b,a)); // build the hash table from the param list 

  keys = Object.keys(states); // get the list of hashtable keys as an array (the 49 states id)
  Scan=(p)=> // Recursive scan function
  {
    var sId = keys[p]; // in sid the current state id, or undefined if passed last key
    if (!sId) return true; // end of keys, recursive search is finished 
    var sInfo = states[sId]; // in sInfo the aarray of adiacent states id + the color property

    return ['blue','gold','red','tan'].some( (color) => // check the four avaialabe colors
      {
        var colorInUse = sInfo.some( (t) => states[t].color == color); // true if an adiacent state already has the currnet color
        if (!colorInUse) // if the color is usable
        {
          sInfo.color = color; // assign the current color to the current state
          var ok = Scan(p+1); // proceed with the recursive scan on the next state
          if (!ok) // if recursive scan failed, backtrack
          {
            sInfo.color = ''; // remove the assigned color for the current state
          }
          return ok;
        }
      }
    )
  },
  Scan(0), // start scan 
  keys.forEach( (sId) => console.log('#'+sId+'{fill:'+states[sId].color+'}')) // output color list
}

Test in FireFox/FireBug console

list = "AL-FL AL-GA AL-MS AL-TN AR-LA AR-MO AR-MS AR-OK AR-TN AR-TX AZ-CA AZ-CO AZ-NM "+
"AZ-NV AZ-UT CA-NV CA-OR CO-KS CO-NE CO-NM CO-OK CO-UT CO-WY CT-MA CT-NY CT-RI "+
"DC-MD DC-VA DE-MD DE-NJ DE-PA FL-GA GA-NC GA-SC GA-TN IA-MN IA-MO IA-NE IA-SD "+
"IA-WI ID-MT ID-NV ID-OR ID-UT ID-WA ID-WY IL-IA IL-IN IL-KY IL-MO IL-WI IN-KY "+
"IN-MI IN-OH KS-MO KS-NE KS-OK KY-MO KY-OH KY-TN KY-VA KY-WV LA-MS LA-TX MA-NH "+
"MA-NY MA-RI MA-VT MD-PA MD-VA MD-WV ME-NH MI-OH MI-WI MN-ND MN-SD MN-WI MO-NE "+
"MO-OK MO-TN MS-TN MT-ND MT-SD MT-WY NC-SC NC-TN NC-VA ND-SD NE-SD NE-WY NH-VT "+
"NJ-NY NJ-PA NM-OK NM-TX NM-UT NV-OR NV-UT NY-PA NY-VT OH-PA OH-WV OK-TX OR-WA "+
"PA-WV SD-WY TN-VA UT-WY VA-WV";
F(list);

Output

#AL{fill:blue}
#FL{fill:gold}
#GA{fill:red}
#MS{fill:gold}
#TN{fill:tan}
#AR{fill:blue}
#LA{fill:red}
#MO{fill:gold}
#OK{fill:red}
#TX{fill:gold}
#AZ{fill:blue}
#CA{fill:gold}
#CO{fill:gold}
#NM{fill:tan}
#NV{fill:tan}
#UT{fill:red}
#OR{fill:blue}
#KS{fill:blue}
#NE{fill:red}
#WY{fill:blue}
#CT{fill:blue}
#MA{fill:gold}
#NY{fill:red}
#RI{fill:red}
#DC{fill:blue}
#MD{fill:gold}
#VA{fill:red}
#DE{fill:red}
#NJ{fill:gold}
#PA{fill:blue}
#NC{fill:blue}
#SC{fill:gold}
#IA{fill:blue}
#MN{fill:gold}
#SD{fill:tan}
#WI{fill:red}
#ID{fill:gold}
#MT{fill:red}
#WA{fill:red}
#IL{fill:tan}
#IN{fill:gold}
#KY{fill:blue}
#MI{fill:blue}
#OH{fill:red}
#WV{fill:tan}
#NH{fill:blue}
#VT{fill:tan}
#ME{fill:gold}
#ND{fill:blue}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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