Skip to content

Instantly share code, notes, and snippets.

@junjis0203
Last active September 28, 2019 10:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save junjis0203/4b26b3ef265879aa264f139f2ec13b5a to your computer and use it in GitHub Desktop.
Save junjis0203/4b26b3ef265879aa264f139f2ec13b5a to your computer and use it in GitHub Desktop.
React and Vue implementation for Mathematical Girls: The Secret Notebook (Bits and Binary), chapter 4
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link type="text/css" href="fliptrip.css" rel="stylesheet">
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<!--
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
-->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
function Stone(props) {
let stoneClassNames = "stone";
stoneClassNames += " ";
stoneClassNames += props.toggle ? "stone-black" : "stone-white";
return (
<div className="stone-wrapper">
<div
className={stoneClassNames}
onClick={() => props.onClick(props.index)}
/>
</div>
);
}
class Board extends React.Component {
renderStones() {
const components = [];
for (let i = 0; i < this.props.stones.length; i++) {
const stone = (
<Stone
key={i}
index={i}
toggle={this.props.stones[i]}
onClick={() => this.props.onClick(i)}
/>
);
components.push(stone)
}
return components;
}
render() {
return (
<div className="board">
{this.renderStones()}
</div>
);
}
};
const arrayEquals = (a, b) => {
if (a.length != b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
};
class Game extends React.Component {
constructor(props) {
super(props);
const stones = Array(this.props.number).fill(false);
const history = [stones.slice()];
this.state = {
history: history,
error: false,
fulltrip: false
};
}
handleClick(i) {
const history = this.state.history;
const current = history[history.length - 1];
const stones = current.slice();
stones[i] = !stones[i];
const error = history.find((h) => arrayEquals(h, stones));
const newHistory = history.concat([stones]);
this.setState({
history: newHistory,
error: error,
fulltrip: !error && newHistory.length == 2 ** this.props.number
});
}
renderMessage() {
const message = this.state.error ? "ERROR!" : (this.state.fulltrip ? "FULLTRIP!" : "");
return (
<span>{message}</span>
);
}
render() {
const history = this.state.history;
const current = history[history.length - 1];
return (
<div className="game">
<Board
stones={current}
onClick={(i) => this.handleClick(i)}
/>
<div className="message">
{this.renderMessage()}
</div>
</div>
);
}
}
const domContainer = document.querySelector('#container');
ReactDOM.render(<Game number={3} />, domContainer);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link type="text/css" href="fliptrip.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--
script src="https://cdn.jsdelivr.net/npm/vue"></script>
-->
</head>
<body>
<div id="app">
</div>
<script>
Vue.component('stone', {
props: ['index'],
data: function() {
return {
toggle: false
};
},
computed: {
classObject: function() {
return {
'stone-black': this.toggle,
'stone-white': !this.toggle
}
}
},
methods: {
handleClick: function() {
this.toggle = !this.toggle;
this.$emit('stone-click', this.index);
}
},
template: `
<div class="stone-wrapper">
<div
class="stone"
v-bind:class="classObject"
v-on:click="handleClick"
/>
</div>
`,
});
Vue.component('board', {
props: ['number'],
watch: {
// reset stone state when number is reassigned
number: function(newNumber, oldNumber) {
for (let c of this.$children) {
c.toggle = false;
}
}
},
methods: {
handleClick: function(i) {
this.$emit('stone-click', i);
}
},
template: `
<div className="board">
<stone
v-for="i in number"
v-bind:key="i"
v-bind:index="i"
v-on:stone-click="handleClick"
></stone>
</div>
`
});
const arrayEquals = (a, b) => {
if (a.length != b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
};
Vue.component('game', {
props: ['number'],
data: function() {
const stones = Array(this.number).fill(false);
const history = [stones.slice()];
return {
history: history,
error: false,
fulltrip: false
};
},
watch: {
// reset game state when number is reassigned
number: function(newNumber, oldNumber) {
const stones = Array(newNumber).fill(false);
this.history = [stones.slice()];
this.fulltrip = this.error = false;
}
},
computed: {
message: function() {
return this.error ? "ERROR!" : (this.fulltrip ? "FULLTRIP!" : "");
}
},
methods: {
handleClick: function(i) {
const current = this.history[this.history.length - 1];
const stones = current.slice();
stones[i] = !stones[i];
this.error = this.history.find((h) => arrayEquals(h, stones));
this.history.push(stones);
this.fulltrip = !this.error && this.history.length == 2 ** this.number;
}
},
template: `
<div class="game">
<board
v-bind:number="number"
v-on:stone-click="handleClick"
/>
<div class="message">
{{ message }}
</div>
</div>
`
});
const app = new Vue({
el: '#app',
data: {
number: 3
},
template: `
<game v-bind:number="number"></game>
`
});
</script>
</body>
</html>
.game {
display: inline-block;
}
.board {
display: inline-block;
}
.stone-wrapper {
display: inline-block;
width: 80px;
height: 80px;
border: 1px solid #000;
}
.stone {
margin: 4px;
width: 72px;
height: 72px;
border-radius: 50%;
border: 1px solid #000;
}
.stone-black {
background: black;
}
.stone-white {
background: white;
}
.message {
text-align: center;
font-weight: bold;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment