text: module.exports=`/**
   * This celullar automaton is extracted from a game I wrote in 2014 for JS13K:
   * https://github.com/gre/ibex
   *
   * Technical article: http://greweb.me/2014/09/ibex-cellular-automata/
   */
  
  //@flow
  import React, { Component } from "react";
  import { Shaders, Node, GLSL, Uniform } from "gl-react";
  import { Surface } from "gl-react-dom";
  import ndarray from "ndarray";
  import timeLoop from "../../HOC/timeLoop";
  
  const shaders = Shaders.create({
    IBEXRender: {
      frag: GLSL\`
  precision highp float;
  varying vec2 uv;
  uniform vec2 size;
  uniform sampler2D state;
  uniform vec3 CL[9];
  
  float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
  }
  vec3 colorFor (int e) {
    if(e==0) return CL[0];
    if(e==1) return CL[1];
    if(e==2) return CL[2];
    if(e==3) return CL[3];
    if(e==4) return CL[4];
    if(e==5) return CL[5];
    if(e==6) return CL[6];
    if(e==7) return CL[7];
    return CL[8];
  }
  int getState (vec2 pos) {
    vec2 uv = (floor(pos) + 0.5) / size;
    bool outOfBound = uv.x<0.0||uv.x>1.0||uv.y<0.0||uv.y>1.0;
    if (outOfBound) return pos.y < 0.0 ? 1 : 0;
    float cel = texture2D(state, uv).r;
    return int(floor(.5 + 9. * cel));
  }
  vec3 stateColorPass (int e, vec2 pos) {
    return (
      mix(1.0, rand(pos), 0.05*float(e==1) + 0.2*float(e==8)) +
      float(e==8) * (
        step(0.97, rand(pos)) * vec3(3.0, 0.0, 0.0) +
        step(rand(pos), 0.02) * vec3(1.5, -0.5, 0.5)
      )
    ) * colorFor(e);
  }
  void main(){
    vec2 statePos = uv * size;
    vec2 statePosFloor = floor(statePos);
    vec3 stateColor = stateColorPass(getState(statePosFloor), statePosFloor);
    vec3 c = stateColor;
    vec2 pixelPos = fract(statePos);
    vec3 pixelColor = -vec3(0.03) * (pixelPos.x - pixelPos.y);
    c += pixelColor;
    gl_FragColor = vec4(c, 1.0);
  }
  \`
    },
    IBEXLogic: {
      frag: GLSL\`
  #define RAND (S_=vec2(rand(S_), rand(S_+9.))).x
  #define AnyADJ(e) (NW==e||SE==e||NE==e||SW==e||NN==e||SS==e||EE==e||WW==e)
  precision highp float;
  int A  = 0;
  int E  = 1;
  int F  = 2;
  int W  = 3;
  int V  = 4;
  int S  = 5;
  int Al = 6;
  int Ar = 7;
  int G  = 8;
  varying vec2 uv;
  uniform vec2 SZ;
  uniform float SD;
  uniform float TI;
  uniform float TS;
  uniform float ST;
  uniform sampler2D state;
  uniform bool RU;
  uniform bool draw;
  uniform ivec2 DP;
  uniform float DR;
  uniform int DO;
  uniform float forestGrowFactor;
  int get (int x_, int y_) {
    vec2 u = (SZ * uv + vec2(x_, y_)) / SZ;
    return (u.x < 0.0 || u.x >= 1.0 || u.y < 0.0 || u.y >= 1.0) ? 0 :
      int(floor(.5 + 9. * texture2D(state, u).r));
  }
  bool between (float f, float a, float b) {
    return a <= f && f <= b;
  }
  float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
  }
  float grassDistrib (vec2 p) {
    return mix(
    rand(vec2(p.x)),
    0.5*(1.0+(cos(sin(p.y*0.01 + p.x * 0.05) +
    (1.0 + 0.3*sin(p.x*0.01)) * p.y * 0.08))),
    0.6
    );
  }
  bool hellTriggerPosition (vec2 p) {
    if (TS==0.0) return false;
    float hellTickStart = 800.0;
    float hellTickInterv = 70.0;
    float hellSize = 5.0;
    float dt = TI - TS - hellTickStart;
    float x = floor(dt / hellTickInterv);
    float y = (dt - x * hellTickInterv);
    return distance(vec2(2.0 * (hellSize * x - ST), hellSize * y), p) <= hellSize;
  }
  void main () {
    vec2 p = uv * SZ;
    vec2 S_ = p + 0.001 * TI;
    int NW = get(-1, 1);
    int NN = get( 0, 1);
    int NE = get( 1, 1);
    int WW = get(-1, 0);
    int CC = get( 0, 0);
    int EE = get( 1, 0);
    int SW = get(-1,-1);
    int SS = get( 0,-1);
    int SE = get( 1,-1);
    bool prevIsSolid = CC==E||CC==G||CC==V||CC==S;
    int r = A;
    int grassMaxHeight = int(20.0 * pow(grassDistrib(p), 1.3));
    float rainRelativeTime = mod(TI, 300.0);
    float volcRelativeTime = mod(TI, 25.0);
    if (
      -0.05 * float(NW==W) + -0.40 * float(NN==W) + -0.05 * float(NE==W) +
      -0.50 * float(WW==W) + -0.50 * float(CC==W) + -0.50 * float(EE==W) +
       0.35 * float(SW==F) +  0.90 * float(SS==F) +  0.35 * float(SE==F)
     >= 0.9 - 0.6 * RAND
     ||
     CC == F && RAND < 0.9 && AnyADJ(G)
    ) {
      r = F;
    }
    if (
      between(
        0.3 * float(NW==W) +  0.9 * float(NN==W) +  0.3 * float(NE==W) +
        0.1 * float(WW==W) + -0.3 * float(CC==F) +  0.1 * float(EE==W) +
                             -0.3 * float(SS==F)
        ,
        0.9 - 0.6 * RAND,
        1.4 + 0.3 * RAND
      )
      ||
      !prevIsSolid &&
      RAND < 0.98 &&
      ( (WW==W||NW==W) && SW==E || (EE==W||NE==W) && SE==E )
      ||
      !prevIsSolid &&
      p.y >= SZ.y-1.0 &&
      rainRelativeTime < 100.0 &&
      between(
        p.x -
        (rand(vec2(SD*0.7 + TI - rainRelativeTime)) * SZ.x)
        ,
        0.0,
        100.0 * rand(vec2(SD + TI - rainRelativeTime))
      )
      ||
      !prevIsSolid && (
        0.9 * float(NW==S) +  1.0 * float(NN==S) +  0.9 * float(NE==S) +
        0.7 * float(WW==S) +                        0.7 * float(EE==S)
        >= 1.0 - 0.3 * RAND
      )
    ) {
      r = W;
    }
    if (CC == E) {
      if (!(WW==A && EE==A)) r = E;
      if (
        RAND < 0.3 && (
          1.0 * float(NW==W) + 1.2 * float(NN==W) + 1.0 * float(NE==W) +
          0.5 * float(WW==W) +                      0.5 * float(EE==W) +
          0.3 * float(SW==W) + 0.2 * float(SS==W) + 0.3 * float(SE==W)
          >= 3.0 - 2.5 * RAND
        )
        ||
        RAND < 0.01 && ( WW==S || NN==S || EE==S )
      ) {
        r = S;
      }
      if (
      RAND < 0.01 + 0.02 * smoothstep(500.0, 5000.0, ST + p.x) &&
      ( int(WW==V) + int(SS==V) + int(EE==V) + int(SE==V) + int(SW==V) > 1 )
      ) {
        r = V;
      }
    }
    if (grassMaxHeight > 0) {
      if (CC == G) {
        r = G;
        if (
        CC == G &&
        RAND < 0.9 && (
          AnyADJ(F) ||
          AnyADJ(V)
        )) {
          r = F;
        }
      }
      else if (!prevIsSolid && (AnyADJ(E) || AnyADJ(G) || AnyADJ(S))) {
        if (RAND < 0.03 * forestGrowFactor &&
          get(0, -grassMaxHeight) != G && (
            SS==G && RAND < 0.07 ||
            SS==E && RAND < 0.02 ||
            AnyADJ(W) ||
            AnyADJ(S)
          )
        ) {
          r = G;
        }
      }
    }
    if ((!prevIsSolid || CC==F) && SS==V) {
      r = F;
    }
    if (CC == V) {
      r = V;
      if (
        NW==W || NN==W || NE==W || EE==W || WW==W
      ) {
        r = RAND < 0.8 ? S : E;
      }
      if (
        RAND<0.005 &&
        ( int(SW==F||SW==V) + int(SS==F||SS==V) + int(SE==F||SE==V) < 2 )
      ) {
        r = E;
      }
      if (
        int(NW==S) + int(SE==S) + int(NE==S) + int(SW==S) +
        int(NN==S) + int(SS==S) + int(EE==S) + int(WW==S)
        > 1
      ) {
        r = RAND < 0.2 ? V : (RAND < 0.8 ? S : E);
      }
    }
    if (prevIsSolid &&
      p.y <= 2.0 &&
      volcRelativeTime <= 1.0 &&
      RAND < 0.3 &&
      between(
        p.x -
        rand(vec2(SD*0.01 + TI - volcRelativeTime)) * SZ.x
        ,
        0.0,
        10.0 * rand(vec2(SD*0.07 + TI - volcRelativeTime))
      )
    ) {
      r = V;
    }
    if (CC == S) {
      r = S;
      if (
        RAND<0.06 &&
        ( int(NW==W||NW==S) + int(NN==W||NN==S) + int(NE==W||NE==S) < 1 )
  
        ||
        ( EE==F || WW==F || SS==F || SW==F || SE==F )
      ) {
        r = E;
      }
      if (AnyADJ(V)) {
        r = RAND < 0.15 ? V : (RAND < 0.6 ? S : E);
      }
    }
    if (r == A) {
      if (RAND < 0.00001) r = Al;
      if (RAND < 0.00001) r = Ar;
    }
  
    int wind = r==Al ? Al : r == Ar ? Ar : 0;
    float f =
  (-0.1+0.05*(RAND-0.5)) * float(NW==Ar) + 0.12                  * float(NE==Al) +
  -0.65                  * float(WW==Ar) + 0.65                  * float(EE==Al) +
  -0.1                   * float(SW==Ar) + (0.2+0.05*(RAND-0.5)) * float(SE==Al) ;
  
    if (between(f, 0.4 * RAND, 0.95)) {
      wind = Al;
    }
    else if (between(f, -0.95, -0.4 * RAND)) {
      wind = Ar;
    }
    if (wind != 0) {
      if (r == A) {
        r = wind;
      }
      else if(r == F) {
        if (RAND < 0.4) r = wind;
      }
      else if (r == W) {
        if (RAND < 0.1) r = wind;
      }
    }
    if (draw) {
      vec2 pos = floor(p);
      if (distance(pos, vec2(DP)) <= DR) {
        if (DO == W) {
          if (prevIsSolid && CC!=G) {
            r = S;
          }
          else if (!prevIsSolid && mod(pos.x + pos.y, 2.0)==0.0) {
            r = W;
          }
        }
        else if (DO == F) {
          r = prevIsSolid ? V : F;
        }
        else {
          r = DO;
        }
      }
    }
    if (hellTriggerPosition(p)) {
      r = prevIsSolid ? V : F;
    }
    if (!RU) {
      if (r == F || r == W|| r == G) r = A;
      if (r == V || r == S) r = E;
    }
    gl_FragColor = vec4(float(r) / 9.0,  0.0, 0.0, 1.0);
  }\`
    }
  });
  
  class IBEXLogic extends Component {
    state = {
      seed: Math.random()
    };
    shouldComponentUpdate({ tick }) {
      return tick !== this.props.tick;
    }
    render() {
      const {
        size,
        tick,
        initialState,
        forestGrowFactor,
        waterFactor,
        fireFactor
      } = this.props;
      const { seed } = this.state;
      let draw = false, drawPosition = [0, 0], drawRadius = 0, drawElement = 0;
  
      let w = Math.random() < waterFactor, f = Math.random() < fireFactor;
      if (w && f) {
        if (Math.random() * waterFactor - Math.random() * fireFactor > 0) {
          f = false;
        } else {
          w = false;
        }
      }
      if (w) {
        draw = true;
        drawPosition = [
          size[0] * Math.random(),
          size[1] * (0.8 + 0.2 * Math.random())
        ];
        drawRadius = 4;
        drawElement = 3;
        console.log(drawElement, drawPosition, drawPosition);
      } else if (f) {
        draw = true;
        drawPosition = [size[0] * Math.random(), 0];
        drawRadius = 4;
        drawElement = 2;
        console.log(drawElement, drawPosition, drawPosition);
      }
  
      return (
        <Node
          shader={shaders.IBEXLogic}
          sync
          backbuffering
          uniformsOptions={{ state: { interpolation: "nearest" } }}
          uniforms={{
            state: tick === 0 ? initialState : Uniform.Backbuffer,
            SZ: size,
            SD: seed,
            TI: tick,
            TS: 0, // tick start
            RU: true, // logic running
            ST: true, // render started
            draw,
            DP: drawPosition, // draw position
            DR: drawRadius, // draw radius
            DO: drawElement, // the element that is being drawn
            forestGrowFactor
          }}
        />
      );
    }
  }
  
  var colors = [
    [0.11, 0.16, 0.23], // 0: air
    [0.74, 0.66, 0.51], // 1: earth
    [0.84, 0.17, 0.08], // 2: fire
    [0.40, 0.75, 0.90], // 3: water
    [0.60, 0.00, 0.00], // 4: volcano (fire spawner)
    [0.30, 0.60, 0.70], // 5: source (water spawner)
    [0.15, 0.20, 0.27], // 6: wind left
    [0.07, 0.12, 0.19], // 7: wind right
    [0.20, 0.60, 0.20] // 8: grass (forest)
  ];
  
  const IBEXRender = ({ size, children: state }) => (
    <Node
      shader={shaders.IBEXRender}
      uniformsOptions={{ state: { interpolation: "nearest" } }}
      uniforms={{
        state,
        size,
        CL: colors
      }}
    />
  );
  
  const Game = timeLoop(
    class extends Component {
      state = {
        tick: 0,
        lastTickTime: this.props.time
      };
  
      componentWillReceiveProps({ time, speed }) {
        this.setState(({ tick, lastTickTime }) => {
          const delta = 1000 / speed;
          if (time - lastTickTime > delta) {
            return {
              tick: tick + 1,
              lastTickTime: lastTickTime + delta
            };
          }
        });
      }
  
      render() {
        const {
          size,
          initialState,
          forestGrowFactor,
          waterFactor,
          fireFactor
        } = this.props;
        const { tick } = this.state;
        return (
          <IBEXRender size={size}>
            <IBEXLogic
              initialState={initialState}
              size={size}
              tick={tick}
              forestGrowFactor={forestGrowFactor}
              waterFactor={waterFactor}
              fireFactor={fireFactor}
            />
          </IBEXRender>
        );
      }
    }
  );
  
  // This should be implemented in a shader (it's a cellular automaton too)
  // but it's how it was done in the game
  function generate(startX: number, worldSize: [number, number]) {
    var worldPixelRawBuf = new Uint8Array(worldSize[0] * worldSize[1] * 4);
    var worldPixelBuf = new Uint8Array(worldSize[0] * worldSize[1]);
    var waterInGeneration = 0;
    var volcanoInGeneration = 0;
    var w = worldSize[0], h = worldSize[1];
    function step(a, b, x) {
      return Math.max(0, Math.min((x - a) / (b - a), 1));
    }
    function affectColor(buf, i, c) {
      buf[i] = ~~(256 * c / 9);
      buf[i + 3] = 1;
    }
    function get(b, x, y) {
      if (x >= 0 && x < w && y >= 0 && y < h) {
        return b[x + y * w];
      }
      return y > 50 ? 1 : 0;
    }
    function set(b, x, y, e) {
      if (x >= 0 && x < w && y >= 0 && y < h) {
        b[x + y * w] = e;
      }
    }
    var K = 26;
    var x, y, i, k, e;
    for (x = startX; x < worldSize[0]; ++x) {
      for (y = 0; y < worldSize[1]; ++y) {
        e = +(Math.random() >
          0.22 +
            0.3 *
              (step(0, 20, y) + step(worldSize[1] - 20, worldSize[1] - 2, y)));
        set(worldPixelBuf, x, y, e);
      }
    }
    var swp = new Uint8Array(worldPixelBuf);
    var cur = worldPixelBuf;
    for (k = 0; k < K; ++k) {
      for (x = startX; x < worldSize[0]; ++x) {
        for (y = 0; y < worldSize[1]; ++y) {
          var me = get(cur, x, y);
          var sum =
            0.1 * me +
            (0.9 + 0.1 * Math.random()) * (get(cur, x - 1, y - 1) ? 1 : 0) +
            (0.9 + 0.1 * Math.random()) * (get(cur, x, y - 1) ? 1 : 0) +
            (0.9 + 0.1 * Math.random()) * (get(cur, x + 1, y - 1) ? 1 : 0) +
            (1.4 + 0.2 * Math.random()) * (get(cur, x - 1, y) ? 1 : 0) +
            (1.1 + 0.2 * Math.random()) * (get(cur, x + 1, y) ? 1 : 0) +
            (1.6 - 0.1 * Math.random()) * (get(cur, x - 1, y + 1) ? 1 : 0) +
            (1.2 - 0.2 * Math.random()) * (get(cur, x, y + 1) ? 1 : 0) +
            (1.0 - 0.1 * Math.random()) * get(cur, x + 1, y + 1 ? 1 : 0);
          let e = +(sum <= 6 + (Math.random() - 0.5) * (1 - k / K));
          if (
            e &&
            sum >= 6 - Math.random() * waterInGeneration + 4 * step(110, 0, y)
          )
            e = 5;
          if (
            e &&
            sum >= 6 - Math.random() * volcanoInGeneration + 6 * step(20, 60, y)
          )
            e = 4;
          set(swp, x, y, e);
        }
      }
      var tmp = swp;
      swp = cur;
      cur = tmp;
    }
    if (swp === cur) worldPixelBuf = swp;
    for (i = 0; i < worldPixelBuf.length; ++i) {
      affectColor(worldPixelRawBuf, 4 * i, worldPixelBuf[i]);
    }
    return ndarray(worldPixelRawBuf, [worldSize[0], worldSize[1], 4])
      .transpose(1, 0, 2)
      .step(1, -1, 1);
  }
  
  const size = [200, 200];
  
  export default class Example extends Component {
    state = {
      initialState: generate(0, size)
    };
    render() {
      const { forestGrowFactor, fireFactor, waterFactor, speed } = this.props;
      const { initialState } = this.state;
      return (
        <Surface width={400} height={400}>
          <Game
            speed={speed}
            size={size}
            initialState={initialState}
            forestGrowFactor={forestGrowFactor}
            fireFactor={fireFactor}
            waterFactor={waterFactor}
          />
        </Surface>
      );
    }
  
    static defaultProps = {
      speed: 60,
      forestGrowFactor: 1,
      fireFactor: 0,
      waterFactor: 0
    };
  }
  
  /**
  * Game Rule Interactions.
  *
  * Each interaction use various probability. Some are very rare, some frequent.
  /!\\ here air means wind /!\\ it is different of empty, the empty space is
  * called "Nothing" aka N)
  *
  * Primary elements: Water, Fire, Earth, Air
  * =======
  * Water + Nothing => fall / slide
  * Fire + Nothing => grow
  * Air + Nothing => move (directional wind)
  * Water + Air => Water is deviated (wind)
  * Fire + Air => Fire decrease
  * Earth + Water => rarely creates Water Source (water infiltration)
  * Earth + Fire => rarely creates Volcano (fire melt ground into lava)
  *
  * Secondary elements: Source, Volcano
  * =========
  * Source + Nothing => creates Water (on bottom).
  * Volcano + Nothing => creates Fire (on top)
  * Volcano + Source => IF source on top of volcano: sometimes creates Ground.
  * OTHERWISE: sometimes creates volcano.
  * Volcano + Water => rarely creates Source.
  * Earth + Volcano => rarely Volcano expand / grow up in the Earth.
  * Earth + Source => rarely Source expand / infiltrate in the Earth.
  * Source + Fire => Source die.
  *
  * Cases where nothing happens:
  * Water + Fire
  * Earth + Nothing
  * Volcano + Fire
  * Volcano + Air
  * Earth + Air
  * Source + Air
  * Source + Water
  */
  `
  