$ npm install
$ npm run
class Game extends React.Component<{}, {}> {
render() {
return (
<Grid />
);
}
}
interface IGridState {
cells: {value: string, modifiable: boolean, error: boolean}[],
isFinished: boolean
}
class Grid extends React.Component<{}, IGridState> {
constructor(props: any) {
super(props);
let cells = new Array(81).fill(null).map(()=>({value:"", modifiable:false, error:false}));
this.state = {
cells: cells,
isFinished: false
};
}
}
componentDidMount() {
this.initFrom(generateStaticGrid());
}
initFrom(values:any) {
assert.ok(values.length === 81);
let cells = this.state.cells;
for (let index = 0; index < 81; index++) {
cells[index].value = values[index] === "." ? "" : values[index];
cells[index].modifiable = values[index] === "." ? true : false;
}
this.updateState(cells) // Check where the errors are and update the component state
}
handleChange(index: number, value: string) {
assert.ok(value === "" || (Number(value) >= 1 && Number(value) <= 9))
assert.ok(index >= 0 && index < 81)
if (!this.state.cells[index].modifiable) {
console.error("Trying to change an non modifiable cell. Should not happen");
}
let cells = this.state.cells;
cells[index].value = value;
this.updateState(cells);
}
renderCell(index: number) {
assert.ok(index >= 0 && index < 81)
return (
<Cell
index={index}
value={this.state.cells[index].value}
onChange={this.state.cells[index].modifiable ? (index:number, value:string) => this.handleChange(index, value) : null}
error={this.state.cells[index].error}
/>
);
}
renderBlock(blockNum: number) {
assert.ok(blockNum >= 0 && blockNum < 9)
let index = blockIndex(blockNum);
return (
<td>
{this.renderCell(index[0])}{this.renderCell(index[1])}{this.renderCell(index[2])}<br />
{this.renderCell(index[3])}{this.renderCell(index[4])}{this.renderCell(index[5])}<br />
{this.renderCell(index[6])}{this.renderCell(index[7])}{this.renderCell(index[8])}
</td>
)
}
render() {
return (
<div className="sudoku">
<div><button onClick={this.reset.bind(this)}>Reset</button></div><br />
<table className="grid">
<tbody>
{[0, 1, 2].map((line) =>
<tr key={line.toString()}>
{this.renderBlock(line * 3)}
{this.renderBlock(line * 3 + 1)}
{this.renderBlock(line * 3 + 2)}
</tr>
)}
</tbody>
</table>
{this.state.isFinished && <h2 className="status" id="status">Sudoku completed</h2>}
</div>
);
}
interface ICellProps {
index: number,
value: string,
onChange: any,
error: boolean
}
onChange(event: any) {
if (event.target.value === "" || validInput.test(event.target.value)) {
this.props.onChange(this.props.index, event.target.value)
} else {
console.error("Invalid input in cell " + this.props.index + " : " + event.target.value)
}
}
render() {
let cellClass = ""
if (this.props.onChange === null) {
cellClass += "locked "
}
if (this.props.error){
cellClass += "wrongvalue "
}
return (
<input
id={String(this.props.index)}
className={cellClass}
maxLength={1}
value={this.props.value}
onChange={this.props.onChange ? (event) => this.onChange(event) : function() { }}
readOnly={this.props.onChange === null}
/>
);
}
$ docker run -d --name couchdb -p 5984:5984 -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password couchdb
Alternatively take a look at the README.md file of C-Service.
$ export COUCHDB_USER=admin COUCHDB_PASSWORD=password
$ npx @concordant/c-service
$ npm i @concordant/c-client
import { client } from '@concordant/c-client';
interface IGameState {
session: any,
collection: any
}
class Game extends React.Component<{}, IGameState> {
constructor(props: any) {
super(props);
let CONFIG = require('../config.json');
let session = client.Session.Companion.connect(CONFIG.dbName, CONFIG.serviceUrl, CONFIG.credentials);
let collection = session.openCollection("sudoku", false);
this.state = {
session: session,
collection: collection
}
}
render() {
return (
<Grid session={this.state.session} collection={this.state.collection} />
);
}
}
import { client } from '@concordant/c-client';
interface IGridProps {
session: any,
collection: any
}
class Grid extends React.Component<IGridProps, IGridState> { ... }
interface IGridState {
mvmap: any,
cells: {value: string, modifiable: boolean, error: boolean}[],
isFinished: boolean
}
constructor(props: any) {
super(props);
let cells = new Array(81).fill(null).map(()=>({value:"", modifiable:false, error:false}));
let mvmap = this.props.collection.open("grid", "MVMap", false, function () {return});
this.state = {
mvmap: mvmap,
cells: cells,
isFinished: false
};
}
reset() {
let cells = this.state.cells;
for (let index = 0; index < 81; index++) {
if (cells[index].modifiable) {
cells[index].value = "";
this.props.session.transaction(client.utils.ConsistencyLevel.None, () => {
this.state.mvmap.setString(index, cells[index].value);
})
}
}
this.updateState(cells)
}
handleChange(index: number, value: string) {
assert.ok(value === "" || (Number(value) >= 1 && Number(value) <= 9))
assert.ok(index >= 0 && index < 81)
if (!this.state.cells[index].modifiable) {
console.error("Trying to change an non modifiable cell. Should not happend");
}
let cells = this.state.cells;
cells[index].value = value;
this.updateState(cells);
this.props.session.transaction(client.utils.ConsistencyLevel.None, () => {
this.state.mvmap.setString(index, value);
})
}
It is important to remember that the app operates on objects within a transaction, i.e., a block of related accesses. You are not allowed to operate on objects outside of a transaction. Note: Concordant will eventually support a choice of transaction consistency levels (a.k.a. isolation levels). However, in the currently available alpha version, we only implement None, i.e., a transaction is not guaranteed to be atomic and cannot abort.
this.props.session.transaction(client.utils.ConsistencyLevel.None, () => {
this.state.mvmap.setString(index, value);
})
function hashSetToString(set: any) {
let res = new Set();
let it = set.iterator();
while (it.hasNext()) {
let val = it.next();
if (val !== "") {
res.add(val);
}
}
return Array.from(res).sort().join(' ')
}
timerID!: NodeJS.Timeout;
componentDidMount() {
this.initFrom(generateStaticGrid());
this.timerID = setInterval(
() => this.updateGrid(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
updateGrid() {
let cells = this.state.cells;
for (let index = 0; index < 81; index++) {
if (cells[index].modifiable) {
cells[index].value = "";
}
}
this.props.session.transaction(client.utils.ConsistencyLevel.None, () => {
let itString = this.state.mvmap.iteratorString()
while(itString.hasNext()) {
let val = itString.next()
cells[val.first].value = hashSetToString(val.second)
}
})
this.updateState(cells)
}
$ npm run