Pen Settings

HTML

CSS

CSS Base

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

Vue

              
                <template>
<header id="header-title">
  <h1><a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" target="_blank">GAME OF LIFE</a></h1>
</header>


<main>
  
  <section>
    <div id="board" :title="staticPattern ? 'Static Pattern Detected!' : undefined">
      <div v-for="(n, i) in matrixLength" @click="selectCell(i)" :class="{ active: matrix[i] }" />
    </div>
    <h2 id="generation">GENERATION<br><mark>{{ generation }}</mark></h2>
  </section>
  
  <section id="actions">
    <button @click="togglePlayPause" :disabled="!interval && (shouldStop || errTime)" id="toggle-button">{{ interval ? "Pause" : "Play" }}</button>
    <button @click="random" :disabled="!matrixLength" id="random-board-button">Random Board</button>
    <button @click="reset" :disabled="boardEmpty && !generation" id="reset-board-button">Reset</button>
  </section>
  
  <section id="settings">
    <div>
      <fieldset :title="errTime ? `>= ${minTime}ms` : undefined">
        <legend>Interval (ms)</legend>
        <input type="number" :min="minTime" v-model.number="time" :disabled="interval"/>
      </fieldset>
      <p>Disabled when it is running</p>
    </div>
    <div>
      <div id="board-size-options">
        <fieldset :title="boardWidth < minBoardSide ? `>= ${minBoardSide}` : undefined">
          <legend>Width</legend>
          <input type="number" :min="minBoardSide" v-model.number="boardWidth" :disabled="disableResize" />
        </fieldset>
        <fieldset :title="boardHeight < minBoardSide ? `>= ${minBoardSide}` : undefined">
          <legend>Height</legend>
          <input type="number" :min="minBoardSide" v-model.number="boardHeight" :disabled="disableResize" />
        </fieldset>
      </div>
      <p>Make sure to <strong @click="reset" id="clear-board-text">clear the board</strong> first!</p>
    </div>
  </section>
  
</main>
</template>



<script>
const LIVE = 1;


export default {
  data() {
    return {
      boardWidth: Math.max(Math.floor((window.innerWidth - 40) / 17), 20),
      boardHeight: Math.max(Math.floor((window.innerHeight - 675) / 17), 20),
      minBoardSide: 1,
      matrix: [],
      //
      generation: 0,
      time: 500,
      minTime: 50,
      //
      interval: undefined
    }
  },
  
  computed: {
    errTime() {
      return this.time < this.minTime;
    },
    matrixLength() {
      return Math.max(this.boardWidth, 0) * Math.max(this.boardHeight, 0);
    },
    boardEmpty() {
      return !this.matrix.includes(LIVE);
    },
    disableResize() {
      return (!this.boardEmpty || this.interval);
    },
    nextMatrix() {
      const { boardWidth } = this,
            MATRIX = [...this.matrix];
      for (let i = 0; i < this.matrixLength; i++) {
        const COL = i % boardWidth,
              NEIGHBOURS = [i - boardWidth, i + boardWidth];
        if (COL !== 0) NEIGHBOURS.push(i - 1 - boardWidth, i - 1, i - 1 + boardWidth);
        if (COL !== (boardWidth - 1)) NEIGHBOURS.push(i + 1 - boardWidth, i + 1, i + 1 + boardWidth);
        const LIVE_NEIGHBOURS = NEIGHBOURS.filter(index => index >= 0 && this.matrix[index]).length;
        if (LIVE_NEIGHBOURS == 3) MATRIX[i] = LIVE;
        else if (LIVE_NEIGHBOURS != 2) delete MATRIX[i];
      }
      return MATRIX;
    },
    staticPattern() {
      return !this.boardEmpty && this.matrix.toString() == this.nextMatrix.toString();
    },
    shouldStop() {
      return this.boardEmpty || this.staticPattern;
    }
  },
  
  methods: {
    selectCell(i) {
      if (this.matrixLength) {
        if (this.matrix[i]) {
          delete this.matrix[i];
        } else {
          this.matrix[i] = LIVE;
        }
      }
    },
    reset() {
      if (confirm("Are you sure to reset the board?")) {
        this.interval = clearInterval(this.interval);
        this.matrix = [];
        this.generation = 0;
      }
    },
    random(ask = true) {
      if (ask && confirm("Are you sure to generate a random board?") || !ask) {
        for (let i = 0; i < this.matrixLength; i++) {
          if (Math.random() < .5) this.matrix[i] = LIVE;
          else delete this.matrix[i];
        }
        while (this.staticPattern || this.boardEmpty) {
          this.selectCell(Math.floor(Math.random() * this.matrixLength));
        }
        this.generation = 0;
      }
    },
    togglePlayPause() {
      this.interval = (this.interval || this.shouldStop) ? clearInterval(this.interval) : setInterval(() => {
        this.matrix = this.nextMatrix;
        this.generation++;
        if (this.shouldStop) {
          this.interval = clearInterval(this.interval);
        }
      }, this.errTime ? this.minTime : this.time);
    }
  },
  
  created() {
    this.random(false);
    this.togglePlayPause();
  }
}
</script>



<style lang="stylus">
body
  color White
  background rgb(5, 10, 15)
  margin 0

#app
  font-family Trebuchet MS, Arial, sans-serif
  min-width min-content
  $px = .5em
  padding 0 $px 3em $px

#header-title > h1:first-child
  text-align center
  > a:first-child
    color SpringGreen
    font-style italic
    text-decoration-style double
    text-decoration-thickness .075em
    transition-duration .15s
    &:hover
      color MediumSpringGreen
    &:active
      text-shadow 0 0 6px

#board
  display grid
  grid-template-columns repeat(v-bind(boardWidth), 1fr)
  gap 1px
  position relative
  background black
  max-width max-content
  padding 2px
  border thin solid DarkGreen
  border-radius 2px
  margin auto
  &[title]
    border-color Crimson
    &::after
      content attr(title)
      position absolute
      top -1em
      right 1em
      font-size .8em
      color OrangeRed
      background rgba(0, 5, 10, .8)
      padding .2em .5em
      border thin solid
      border-radius 4px
      pointer-events none
  > div
    $side = clamp(.94em, 2vw, 1em)
    width $side
    height $side
    cursor pointer
    border-radius 1px
    &:hover
      background rgb(40, 45, 50)
    &.active
      background LimeGreen
      &:hover
        background SeaGreen
 
#generation
  text-align center
  margin-top .5em
  margin-bottom 0
  > mark
     color LimeGreen
     background none

#actions
  display grid
  max-width 18em
  gap .25em
  margin 1.5em auto
  > button
    font-family inherit
    font-variant small-caps
    font-size 1.5em
    font-weight bold
    padding .25em 1em
    border thin solid
    border-radius 4px
    transition-duration .15s
    &:enabled
      background Black
      cursor pointer
      &:hover
        color Black
      &:active
        scale .95
    &:disabled
      color Silver
      cursor not-allowed

coolButton($color)
  color $color
  border-color $color
  &:hover
    background $color
    box-shadow 0 0 8px $color

#toggle-button:enabled
  coolButton(v-bind("interval ? 'Yellow' : 'MediumSpringGreen'"))

#random-board-button:enabled
  coolButton(Orange)

#reset-board-button:enabled
  coolButton(Crimson)

#settings
  display grid
  justify-content center
  gap 1.5em
  > div
    background-color rgb(10, 15, 20)
    padding 1em
    border-radius 8px
    box-shadow 0 0 4px LightSeaGreen
    > p:nth-child(2)
      text-align center
      color: LightSteelBlue
      margin-bottom 0
      &::before
        content "*"
        color Crimson
      > strong[onclick]
        text-decoration underline
  fieldset
    position relative
    background rgb(15, 25, 40)
    padding 0
    border-color DarkSlateBlue
    border-radius 8px
    transition background .15s
    &[title]
      border-color Crimson
      > legend
        color Crimson
      &::after
        content attr(title)
        position absolute
        top -1.5em
        right 1em
        font-size .75em
        color OrangeRed
        background rgba(0, 5, 10, .8)
        padding .1em .5em
        border thin solid
        border-radius 4px
        pointer-events none
    &:focus-within
      background-color rgb(0, 5, 10)
    > legend
      color CornflowerBlue
      font-style italic
      margin 0 .5em
    input
      font 2em monospace
      background-color transparent
      min-width 100%
      width 4.75em
      box-sizing border-box
      padding .25em .5em
      border none
      &:enabled
        color PaleTurquoise
      &:focus
        outline none
      &:disabled
        cursor not-allowed

#board-size-options
  display flex
  flex-wrap wrap
  justify-content center
  gap .25em
 
#clear-board-text
  color MediumSpringGreen
  cursor pointer
  &:hover
    text-decoration underline Teal
</style>
              
            
!
999px

Console