Commit 0d17ecc5 authored by Brady James Garvin's avatar Brady James Garvin
Browse files

Initial commit.

parents
# Disable line-ending conversions for this repository.
* -text
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
[submodule "stylelint-config"]
path = stylelint-config
url = git@git.unl.edu:soft-core/soft-260/stylelint-config.git
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:soft-core/soft-260/eslint-config.git
This diff is collapsed.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
This diff is collapsed.
{
"name": "@unlsoft/boost-app",
"version": "2.0.0",
"description": "A PWA for playing the board game Boost.",
"private": true,
"license": "UNLICENSED",
"scripts": {
"generate:game:demo": "generate-boost-game --demo > ./src/games/demo.js",
"generate:game:two-player": "generate-boost-game > ./src/games/twoPlayer.js",
"generate": "run-p generate:**",
"prelint": "run-s generate",
"lint:css": "stylelint **/*.css **/*.module.css",
"lint:js": "eslint --max-warnings 0 ./src",
"lint": "run-s --continue-on-error lint:**",
"pretest-once": "run-s generate",
"test-once": "react-scripts test --watchAll=false --coverage",
"pretest": "run-s generate",
"test": "react-scripts test --watchAll --coverage",
"prestart": "run-s generate",
"start": "react-scripts start",
"prebuild": "run-s generate",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"homepage": ".",
"dependencies": {
"@reduxjs/toolkit": "^1.6.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.1.9",
"@unlsoft/boost-engine": "file:../boost-engine",
"@unlsoft/boost-game": "file:../boost-game",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-flip-toolkit": "^7.0.13",
"react-modal": "^3.14.3",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-transition-group": "^4.4.2",
"redux-persist": "^6.0.0",
"reselect-map": "^1.0.6",
"workbox-background-sync": "^5.1.3",
"workbox-broadcast-update": "^5.1.3",
"workbox-cacheable-response": "^5.1.3",
"workbox-core": "^5.1.3",
"workbox-expiration": "^5.1.3",
"workbox-google-analytics": "^5.1.3",
"workbox-navigation-preload": "^5.1.3",
"workbox-precaching": "^5.1.3",
"workbox-range-requests": "^5.1.3",
"workbox-routing": "^5.1.3",
"workbox-strategies": "^5.1.3",
"workbox-streams": "^5.1.3"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"@unlsoft/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^3.9.0",
"stylelint": "^13.13.1"
},
"stylelint": {
"extends": "@unlsoft/stylelint-config"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"plugin:jest-dom/recommended",
"plugin:testing-library/react",
"@unlsoft/eslint-config/react"
]
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/widgets/**/*.js",
"src/features/**/*.js",
"src/variant.js"
],
"resetMocks": false,
"restoreMocks": false
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
../node_modules/@unlsoft/boost-engine/dist/engine.js
\ No newline at end of file
../node_modules/@unlsoft/boost-engine/dist/engineThread.js
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta
name="description"
content="Boost is a turn-based abstract strategy board game like checkers, chess, Xiangqi, or Shōgi."
/>
<meta name="theme-color" content="rgba(239 239 239 / 100%)" />
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.svg" />
<title>Boost</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<rect x="0" y="0" width="512" height="512" fill="rgba(0, 0, 0, 1)" />
<path d="M 256 416
C 316 399 365 304 365 147
C 331 177 274 126 256 96
C 238 126 181 177 147 147
C 147 304 196 399 256 416
z" fill="rgba(255, 255, 255, 1)" stroke="rgba(255, 255, 255, 1)" stroke-width="12" stroke-linejoin="round" />
<path d="M 256 352
C 292 342 321 285 321 191
C 301 209 267 178 256 160
C 245 178 211 209 191 191
C 191 285 220 342 256 352
z" fill="rgba(0, 0, 0, 1)" stroke="rgba(0, 0, 0, 1)" stroke-width="12" stroke-linejoin="round" />
</svg>
{
"short_name": "Boost",
"name": "Boost",
"description": "Boost is a turn-based abstract strategy board game like checkers, chess, Xiangqi, or Shōgi.",
"categories": [
"entertainment",
"games",
"social"
],
"icons": [
{
"src": "logo.svg",
"type": "image/svg+xml",
"sizes": "192x192 512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "rgba(239, 239, 239, 1)",
"background_color": "rgba(239, 239, 239, 1)"
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`the Variant component renders two game menus, a players menu, a board for play, an editor, and an analysis board 1`] = `
<div>
<div>
[Page path=
/mno
classNames=
efgh
]
<div>
[Heading text=
Choose Game
]
</div>
<div>
[GameMenu slot=
def
gameIdentifier=
jkl
dragons=
9
defaultPlayers=
[{"type":"HUMAN","assistance":12},{"type":"ENGINE","strength":100}]
newGameTo=
/pqr
loadGameTo=
/stu
]
</div>
</div>
<div>
[Page path=
/pqr
classNames=
efgh
]
<div>
[Heading text=
Choose Players
]
</div>
<div>
[PlayersMenu slot=
def
to=
/stu
]
</div>
</div>
<div>
[Page path=
/stu
classNames=
efgh
]
<div>
[Heading text=
abc
]
</div>
<div>
[Player slot=
def
analysisOnly=
false
analysisDepth=
99
editGameSlot=
ghi
editGameTo=
/yz
analyzeGameSlot=
ghi
analysisPlayers=
[{"type":"HUMAN","assistance":null},{"type":"HUMAN","assistance":null}]
analyzeGameTo=
/abcd
playGameSlot=
playGameTo=
]
</div>
</div>
<div>
[Page path=
/vwx
classNames=
efgh
]
<div>
[Heading text=
Choose Game
]
</div>
<div>
[GameMenu slot=
ghi
gameIdentifier=
jkl
dragons=
9
defaultPlayers=
[{"type":"HUMAN","assistance":null},{"type":"HUMAN","assistance":null}]
newGameTo=
/yz
loadGameTo=
/abcd
]
</div>
</div>
<div>
[Page path=
/yz
classNames=
efgh
]
<div>
[Heading text=
abc
]
</div>
<div>
[Editor slot=
ghi
analyzeGameTo=
/abcd
playGameSlot=
def
playGameTo=
/pqr
]
</div>
</div>
<div>
[Page path=
/abcd
classNames=
efgh
]
<div>
[Heading text=
abc
]
</div>
<div>
[Player slot=
ghi
analysisOnly=
true
analysisDepth=
99
editGameSlot=
editGameTo=
/yz
analyzeGameSlot=
analysisPlayers=
analyzeGameTo=
playGameSlot=
def
playGameTo=
/pqr
]
</div>
</div>
</div>
`;
.subpage-enter {
opacity: 0%;
}
.subpage-enter.subpage-enter-active {
opacity: 100%;
transition: opacity var(--animation-duration);
}
.subpage-exit {
opacity: 100%;
}
.subpage-exit.subpage-exit-active {
opacity: 0%;
transition: opacity var(--animation-duration);
}
@media not (prefers-reduced-motion) {
.subpage-enter {
transform: scale(0.5);
}
.subpage-enter.subpage-enter-active {
transform: scale(1);
transition: transform var(--animation-duration);
}
.subpage-exit {
transform: scale(1);
}
.subpage-exit.subpage-exit-active {
transform: scale(0.5);
transition: transform var(--animation-duration);
}
}
import { useHistory, Route, Redirect } from 'react-router-dom';
import { Page } from './widgets/page.js';
import { Heading } from './widgets/heading.js';
import { createModalOpener } from './widgets/modal.js';
import { Menu, MainMenuButton } from './widgets/menu.js';
import { Tutorial } from './features/tutorial/tutorial.js';
import { ResetTutorialMenu } from './features/tutorial/resetTutorialMenu.js';
import { RULES_TUTORIAL } from './tutorials/rules.js';
import { Variant } from './variant.js';
import './app.css';
import rulesIcon from './icons/rulesWithShadow.svg';
import twoPlayerIcon from './icons/twoPlayerWithShadow.svg';
import analysisIcon from './icons/analysisWithShadow.svg';
const TUTORIAL_RESET_SUBPATH = 'resetRulesTutorial';
export function App() {
const history = useHistory();
return (
<>
<Route exact path={'/'}>
<Redirect to={'/menu'}/>
</Route>
<Page path={'/menu'}>
<Heading text={'Boost'} />
<Menu>
<MainMenuButton
to={'/learn/rules'}
image={rulesIcon}
text={'Learn the Rules'}
onContextMenu={createModalOpener(history, TUTORIAL_RESET_SUBPATH)} />
<MainMenuButton
to={'/play/gameMenu'}
image={twoPlayerIcon}
text={'Play a Game'} />
<MainMenuButton
to={'/analyze/gameMenu'}
image={analysisIcon}
text={'Edit/Analyze a Position'} />
</Menu>
<ResetTutorialMenu subpath={TUTORIAL_RESET_SUBPATH} slot={'learn/rules'} tutorial={RULES_TUTORIAL} />
</Page>
<Page path={'/learn/rules'} classNames={'subpage'}>
<Heading text={RULES_TUTORIAL.title} />
<Tutorial slot={'learn/rules'} tutorial={RULES_TUTORIAL} />
</Page>
<Variant
title={'Boost'}
playSlot={'twoPlayer/play'}
analysisSlot={'twoPlayer/analysis'}
gameIdentifier={'boost-9-9-2-8-2'}
dragons={7}
analysisDepth={5}
playGameMenuPath={'/play/gameMenu'}
playPlayersMenuPath={'/play/playersMenu'}
playBoardPath={'/play/board'}
analysisGameMenuPath={'/analyze/gameMenu'}
analysisEditorPath={'/analyze/editor'}
analysisBoardPath={'/analyze/board'}
classNames={'subpage'} />
</>
);
}
import { combineReducers, getDefaultMiddleware, configureStore } from '@reduxjs/toolkit';
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import tutorialProgressSlice from '../features/tutorial/tutorialProgressSlice.js';
import treeSlotsSlice from '../features/lobby/treeSlotsSlice.js';
import gameTreesSlice from '../features/play/gameTreesSlice.js';
import piecesSlice from '../features/preferences/piecesSlice.js';
const persistedReducer = persistReducer(
{
key: 'root',
storage,
},
combineReducers({
[tutorialProgressSlice.name]: tutorialProgressSlice.reducer,
[treeSlotsSlice.name]: treeSlotsSlice.reducer,
[gameTreesSlice.name]: gameTreesSlice.reducer,
[piecesSlice.name]: piecesSlice.reducer,
}),
);
export const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware({
immutableCheck: false,
// If you want to enable serializableCheck, then set its value to the object
// {
// ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
// }
// instead of true, where those constants are imported from redux-persist
// (see https://github.com/rt2zz/redux-persist/issues/988).
serializableCheck: false,
}),
});
export const persistor = persistStore(store);
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`the GameMenu component creates a new game preview if there is none 1`] = `
<div>
<div
id="portrait"
>
<div
id="app"
>
<div
class="menu bottom-aligned"
>
<div
class="scrollable"
>
<div
class="list"
>
<button
disabled=""
>
[No saved games available.]
</button>
<button>
<div>
[Board treeName=
defPreview
disabled=
true
analysisOnly=
false
]
</div>
New Game
</button>
</div>
</div>
</div>
<div
class="button-bar"
>
<button
class=""
>
<img
alt="Menu"
src="menu.svg"
/>
</button>
</div>
</div>
</div>
</div>
`;
exports[`the GameMenu component deletes saved games and closes the modals 1`] = `
<div>
<div
id="portrait"
>
<div
id="app"
>
<div
class="menu bottom-aligned"
>
<div
class="scrollable"
>
<div
class="list"
>
<button>