diff options
Diffstat (limited to 'src/Snow.tsx')
-rw-r--r-- | src/Snow.tsx | 268 |
1 files changed, 261 insertions, 7 deletions
diff --git a/src/Snow.tsx b/src/Snow.tsx index 47a1c06..9ab4dfd 100644 --- a/src/Snow.tsx +++ b/src/Snow.tsx @@ -1,23 +1,277 @@ import React from 'react'; import MGrid from '@material-ui/core/Grid'; -import { Theme, withStyles } from '@material-ui/core/styles'; +import { Theme, withStyles, StyleRules } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableRow from '@material-ui/core/TableRow'; +import TableCell from '@material-ui/core/TableCell'; +import Button from '@material-ui/core/Button'; +import Slider from '@material-ui/lab/Slider'; +import Grid from './Grid'; -interface SnowProps { - classes: {} -} - -const styles = (theme: Theme) => ({ +const styles = (theme: Theme): StyleRules => ({ + inputLabel: { + fontSize: 16, + paddingRight: 0, + textAlign: 'right' + }, + inputValue: { + textAlign: 'left' + }, buttonSpacer: { marginBottom: theme.spacing.unit * 4, }, + bottomButtons: { + marginTop: 10, + textAlign: 'center', + minWidth: 650 + }, + slider: { + padding: '22px 0px', + } }); +interface SnowProps { + classes: { + inputLabel: string, + inputValue: string, + buttonSpacer: string, + bottomButtons: string, + slider: string + } +} + +function getRandomInt(max: number) { + return Math.floor(Math.random() * Math.floor(max)); +} + +function getRandomSubarray(arr: number[], size: number) { + let shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index; + while (i-- > min) { + index = Math.floor((i + 1) * Math.random()); + temp = shuffled[index]; + shuffled[index] = shuffled[i]; + shuffled[i] = temp; + } + return shuffled.slice(min); +} + class Snow extends React.Component<SnowProps> { + + static genMatrix(n = 20) { + let d = []; + for (let i = 0; i < n; i++) + { + let r = []; + for (let j = 0; j < n; j++) + r.push({d: [0, 0], col: getRandomInt(2)}); + d.push(r); + } + return d; + } + + state = { + colorMatrix: Snow.genMatrix(), + k: 10, + alpha: 8, + nodesPerTick: 10, + maxInactiveTicks: 200, + ticking: false, + simulationSpeed: 100 + }; + + config = { + k: 10, + alpha: 8, + nodesPerTick: 10, + maxInactiveTicks: 200, + inactiveTicks: 0 + }; + + getNodeState(n: number, u: number) { + let r = Math.floor(u / n); + let c = u % n; + return this.state.colorMatrix[r][c]; + } + + setNodeState(n: number, u: number, s: {d: number[], col: number}) { + let r = Math.floor(u / n); + let c = u % n; + let m = [...this.state.colorMatrix]; + m[r][c] = s; + this.setState({colorMatrix: m}); + } + + tick(n: number, m: number) { + let N = n * n; + let active = false; + for (let i = 0; i < m; i++) + { + let u = getRandomInt(N); + let peers = []; + for (let j = 0; j < N; j++) + if (j != u) peers.push(j); + let sample = getRandomSubarray(peers, this.config.k); + let cnt = [0, 0]; + sample.forEach((v) => { + let ss = this.getNodeState(n, v); + cnt[ss.col]++; + }); + let s = this.getNodeState(n, u); + for (let c = 0; c < 2; c++) + { + if (cnt[c] > this.config.alpha) + { + s.d[c]++; + if (s.d[c] > s.d[s.col]) + { + if (s.col != c) active = true; + s.col = c; + this.setNodeState(n, u, s); + } + } + } + } + return active; + } + + pauseTick() { + this.setState({ticking: false}); + } + + startTick() { + this.config.alpha = this.state.alpha; + this.config.k = this.state.k; + this.config.nodesPerTick = this.state.nodesPerTick; + this.config.inactiveTicks = 0; + this.config.maxInactiveTicks = this.state.maxInactiveTicks; + this.autoTick(); + } + + autoTick() { + this.setState({ticking: true}); + setTimeout(() => { + let active = this.tick(20, this.config.nodesPerTick); + console.log(active); + if (!active) + { + if (++this.config.inactiveTicks > this.config.maxInactiveTicks) + { + this.pauseTick(); + return; + } + } + else + this.config.inactiveTicks = 0; + if (this.state.ticking) this.autoTick(); + }, 1000 / this.state.simulationSpeed); + } + + reset() { + this.setState({ + colorMatrix: Snow.genMatrix(), + ticking: false + }); + } + render() { const { classes } = this.props; return ( - <MGrid container spacing={16} style={{minWidth: 700}}> + <MGrid container spacing={16} style={{minWidth: 600, maxWidth: 1600}}> + <MGrid item lg={6} xs={12}> + <Grid data={this.state.colorMatrix} /> + </MGrid> + <MGrid item lg={6} xs={12}> + <Table> + <TableBody> + <TableRow> + <TableCell className={classes.inputLabel}> + k = + </TableCell> + <TableCell> + <TextField + inputProps={{ className: classes.inputValue } as React.CSSProperties} + value={this.state.k} + disabled={this.state.ticking} + onChange={event => this.setState({k: event.target.value})}/> + </TableCell> + </TableRow> + <TableRow> + <TableCell className={classes.inputLabel}> + alpha = + </TableCell> + <TableCell> + <TextField + inputProps={{ className: classes.inputValue } as React.CSSProperties} + value={this.state.alpha} + disabled={this.state.ticking} + onChange={event => this.setState({alpha: event.target.value})}/> + </TableCell> + </TableRow> + <TableRow> + <TableCell className={classes.inputLabel}> + nodesPerTick = + </TableCell> + <TableCell> + <TextField + inputProps={{ className: classes.inputValue } as React.CSSProperties} + value={this.state.nodesPerTick} + disabled={this.state.ticking} + onChange={event => this.setState({nodesPerTick: event.target.value})}/> + </TableCell> + </TableRow> + <TableRow> + <TableCell className={classes.inputLabel}> + maxInactiveTicks = + </TableCell> + <TableCell> + <TextField + inputProps={{ className: classes.inputValue } as React.CSSProperties} + value={this.state.maxInactiveTicks} + disabled={this.state.ticking} + onChange={event => this.setState({maxInactiveTicks: event.target.value})}/> + </TableCell> + </TableRow> + <TableRow> + <TableCell className={classes.inputLabel}> + simulationSpeed + </TableCell> + <TableCell> + <Slider + classes={{ container: classes.slider }} + value={this.state.simulationSpeed} + min={1} + max={1000} + onChange={(_, value) => this.setState({simulationSpeed: value})} /> + </TableCell> + </TableRow> + </TableBody> + </Table> + <div className={classes.buttonSpacer} /> + <div className={classes.bottomButtons}> + <MGrid container spacing={16}> + <MGrid item lg={4} xs={12}> + <Button + variant="contained" color="primary" + onClick={event => this.startTick()} + disabled={this.state.ticking}>Start</Button> + </MGrid> + <MGrid item lg={4} xs={12}> + <Button + variant="contained" color="primary" + onClick={event => this.pauseTick()} + disabled={!this.state.ticking}>Pause</Button> + </MGrid> + <MGrid item lg={4} xs={12}> + <Button + variant="contained" color="primary" + onClick={event => this.reset()}>Reset</Button> + </MGrid> + </MGrid> + </div> + </MGrid> </MGrid> ); } |