Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
1 result

Target

Select target project
  • hain5252/boost-board-game
  • efox6/boost-board-game-2020
  • soft-core/soft-260/boost-board-game
  • jadengoter/boost-board-game
  • michael.westberg/boost-board-game
  • mthomas41/boost-board-game
6 results
Select Git revision
  • master
1 result
Show changes
Commits on Source (5)
Showing
with 617 additions and 0 deletions
# 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 = https://git.unl.edu/soft-core/soft-260/stylelint-config.git
[submodule "eslint-config"]
path = eslint-config
url = https://git.unl.edu/soft-core/soft-260/eslint-config.git
This diff is collapsed.
# NETWORKING
################################################################################
# `REACT_APP_SERVER_URL` is the URL where the server is listening for websocket
# connections, including the port. `ws://localhost:4000` is the typical choice
# during development, but in production the `localhost` should be replaced with
# the server's domain name or IP address, and the port should match the `PORT`
# variable in the multiplayer server's `.env` file.
REACT_APP_SERVER_URL=ws://localhost:4000
# IMPORTANT!
################################################################################
# You should always change all of these variables before building the app for
# deployment to production.
# NETWORKING
################################################################################
# `REACT_APP_SERVER_URL` is the URL where the server is listening for websocket
# connections, including the port. `ws://localhost:4000` is the typical choice
# during development, but in production the `localhost` should be replaced with
# the server's domain name or IP address, and the port should match the `PORT`
# variable in the multiplayer server's `.env` file.
REACT_APP_SERVER_URL=ws://localhost:4000
# 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": "3.0.0",
"description": "A PWA for playing the board game Boost.",
"private": true,
"license": "UNLICENSED",
"scripts": {
"generate:game:demo": "generate-boost-game --demo --output-file ./src/games/demo.js",
"generate:game:two-player": "generate-boost-game --output-file ./src/games/twoPlayer.js",
"generate": "run-p generate:**",
"prelint": "run-s generate",
"lint:css": "stylelint \"**/*.css\" \"**/*.module.css\" \"!coverage/**\" \"!build/**\"",
"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 && workbox injectManifest",
"preproduction": "run-s build",
"production": "npx serve --no-clipboard build",
"eject": "react-scripts eject"
},
"homepage": ".",
"dependencies": {
"@reduxjs/toolkit": "^1.8.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.3.0",
"@unlsoft/boost-engine": "file:../boost-engine",
"@unlsoft/boost-game": "file:../boost-game",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-flip-toolkit": "^7.0.14",
"react-modal": "^3.15.1",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1",
"react-switch": "^7.0.0",
"react-transition-group": "^4.4.2",
"redux-persist": "^6.0.0",
"reselect-map": "^1.0.6",
"workbox-background-sync": "^6.5.3",
"workbox-broadcast-update": "^6.5.3",
"workbox-cacheable-response": "^6.5.3",
"workbox-cli": "^6.5.3",
"workbox-core": "^6.5.3",
"workbox-expiration": "^6.5.3",
"workbox-navigation-preload": "^6.5.3",
"workbox-precaching": "^6.5.3",
"workbox-range-requests": "^6.5.3",
"workbox-routing": "^6.5.3",
"workbox-strategies": "^6.5.3",
"workbox-streams": "^6.5.3"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"@unlsoft/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^4.0.2",
"stylelint": "^14.9.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"
],
"ignorePatterns": [
"**/games/*.js"
]
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/client.js",
"src/page.js",
"src/widgets/**/*.js",
"src/features/**/*.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"
]
}
}
boost-app/public/badge.png

13 KiB

../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
boost-app/public/favicon.ico

1.12 KiB

<!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="#efefef" />
<link rel="icon" href="%PUBLIC_URL%/logo.png" />
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-startup-image" href="%PUBLIC_URL%/logo.png" />
<link rel="apple-touch-startup-image" 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>
boost-app/public/logo.png

16.7 KiB

<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"
},
{
"src": "logo.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#efefef",
"background_color": "#efefef"
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`the Page component renders a styled div 1`] = `
<div>
<div
class="page"
>
abc
</div>
</div>
`;
nav {
flex: 1 1 auto;
display: flex;
flex-flow: column nowrap;
justify-content: space-around;
align-content: center;
align-items: center;
margin: var(--spacing);
margin-top: 0;
}
nav > div:last-child {
height: 25%;
}
.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);
}
import { useNavigate, Routes, Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useClientIdentity } from './client.js';
import { Page } from './page.js';
import { Heading } from './widgets/heading.js';
import { ButtonBar, Button } from './widgets/buttonBar.js';
import { Tutorial } from './features/tutorial/tutorial.js';
import { RULES_TUTORIAL } from './tutorials/rules.js';
import { NotificationHandler } from './features/lobby/notificationHandler.js';
import { Account } from './features/lobby/account.js';
import { Invitation } from './features/lobby/invitation.js';
import { GameMenu } from './features/lobby/gameMenu.js';
import { SettingsMenu } from './features/lobby/settingsMenu.js';
import { Player } from './features/play/player.js';
import { Editor } from './features/play/editor.js';
import { AbruptShutdownRecovery } from './features/play/abruptShutdownRecovery.js';
import { Network } from './features/play/network.js';
import { OneGameMonitor } from './features/play/monitor.js';
import './app.css';
import homeIcon from './icons/home.svg';
import rulesIcon from './icons/rules.svg';
import localIcon from './icons/local.svg';
import editIcon from './icons/edit.svg';
import analysisIcon from './icons/analysis.svg';
import remoteIcon from './icons/remote.svg';
import GAME_LIBRARY from './gameLibrary.js';
const GAME = GAME_LIBRARY.get('boost-9-9-2-8-2');
const DRAGON_COUNT = 7;
const ANALYSIS_DEPTH = 5;
function PageWithNavigationBar(props) {
const {
homeAltText,
homeTo,
rulesAltText,
rulesTo,
localAltText,
localTo,
editAltText,
editTo,
analysisAltText,
analysisTo,
remoteAltText,
remoteTo,
} = {
homeAltText: 'Home',
homeTo: '/',
rulesAltText: 'Learn the Rules',
rulesTo: '/rules',
localAltText: 'Play a Game on this Device',
localTo: '/local/gameMenu',
editAltText: 'Edit a Position',
editTo: '/edit/gameMenu',
analysisAltText: 'Analyze a Game',
analysisTo: '/analysis/gameMenu',
remoteAltText: 'Play a Network Game',
remoteTo: '/remote/gameMenu',
...props,
};
const navigate = useNavigate();
return (
<Page>
<ButtonBar>
<Button
image={homeIcon}
altText={homeAltText}
disabled={homeTo === undefined}
onClick={homeTo !== undefined ? () => navigate(homeTo) : undefined} />
<Button
image={rulesIcon}
altText={rulesAltText}
disabled={rulesTo === undefined}
onClick={rulesTo !== undefined ? () => navigate(rulesTo) : undefined} />
<Button
image={localIcon}
altText={localAltText}
disabled={localTo === undefined}
onClick={localTo !== undefined ? () => navigate(localTo) : undefined} />
<Button
image={editIcon}
altText={editAltText}
disabled={editTo === undefined}
onClick={editTo !== undefined ? () => navigate(editTo) : undefined} />
<Button
image={analysisIcon}
altText={analysisAltText}
disabled={analysisTo === undefined}
onClick={analysisTo !== undefined ? () => navigate(analysisTo) : undefined} />
<Button
image={remoteIcon}
altText={remoteAltText}
disabled={remoteTo === undefined}
onClick={remoteTo !== undefined ? () => navigate(remoteTo) : undefined} />
</ButtonBar>
{props.children}
</Page>
);
}
PageWithNavigationBar.propTypes = {
homeAltText: PropTypes.string,
homeTo: PropTypes.string,
rulesAltText: PropTypes.string,
rulesTo: PropTypes.string,
localAltText: PropTypes.string,
localTo: PropTypes.string,
editAltText: PropTypes.string,
editTo: PropTypes.string,
analysisAltText: PropTypes.string,
analysisTo: PropTypes.string,
remoteAltText: PropTypes.string,
remoteTo: PropTypes.string,
};
export function App() {
const clientIdentity = useClientIdentity();
const invitationTarget = clientIdentity !== undefined ? '/remote/invitation' : '/remote/invitationLogin';
const navigate = useNavigate();
const home =
<PageWithNavigationBar homeTo={undefined}>
<nav>
<Heading text={'Boost Board Game'} />
<ButtonBar>
<Button text={'Learn the Rules'} autofocus={true} onClick={() => navigate('/rules')} />
</ButtonBar>
<ButtonBar>
<Button text={'Play a Game on this Device'} onClick={() => navigate('/local/gameMenu')} />
</ButtonBar>
<ButtonBar>
<Button text={'Play a Network Game'} onClick={() => navigate('/remote/gameMenu')} />
</ButtonBar>
<ButtonBar>
<Button text={'Accept a Game Invitation'} onClick={() => navigate(invitationTarget)} />
</ButtonBar>
<div />
</nav>
</PageWithNavigationBar>;
const rulesTutorial =
<PageWithNavigationBar rulesTo={undefined}>
<Heading text={RULES_TUTORIAL.title} />
<Tutorial slot={'rules'} tutorial={RULES_TUTORIAL} />
</PageWithNavigationBar>;
const localGameMenu =
<PageWithNavigationBar localTo={undefined}>
<GameMenu
slot={'local'}
gameIdentifier={GAME.identifier}
dragons={DRAGON_COUNT}
remote={false}
newGameTo={'/local/settingsMenu'}
loadGameTo={'/local'} />
</PageWithNavigationBar>;
const localSettingsMenu =
<PageWithNavigationBar>
<Heading text={'Choose Settings'} />
<SettingsMenu slot={'local'} remote={false} backTo={'/local/gameMenu'} to={'/local'} />
</PageWithNavigationBar>;
const localBoard =
<PageWithNavigationBar
localAltText={'Play another Game on this Device'}
editAltText={'Edit this Position'}
editTo={'/edit'}
analysisAltText={'Analyze from this Position'}
analysisTo={'/analysis'}>
<Heading text={'Local Game'} />
<Player
slot={'local'}
remote={false}
analysisOnly={false}
analysisDepth={ANALYSIS_DEPTH}
settingsTo={'/local/settingsMenu'} />
</PageWithNavigationBar>;
const editGameMenu =
<PageWithNavigationBar editTo={undefined}>
<GameMenu
slot={'local'}
gameIdentifier={GAME.identifier}
dragons={DRAGON_COUNT}
remote={false}
newGameTo={'/edit'}
loadGameTo={'/edit'} />
</PageWithNavigationBar>;
const editBoard =
<PageWithNavigationBar
localAltText={'Play from this Position'}
localTo={'/local'}
editAltText={'Edit another Position'}
analysisAltText={'Analyze from this Position'}
analysisTo={'/analysis'}>
<Heading text={'Editor'} />
<Editor slot={'local'} />
</PageWithNavigationBar>;
const analyzeGameMenu =
<PageWithNavigationBar analysisTo={undefined}>
<GameMenu
slot={'local'}
gameIdentifier={GAME.identifier}
dragons={DRAGON_COUNT}
remote={false}
newGameTo={'/analysis'}
loadGameTo={'/analysis'} />
</PageWithNavigationBar>;
const analyzeBoard =
<PageWithNavigationBar
localAltText={'Play from this Position'}
localTo={'/local'}
editAltText={'Edit this Position'}
editTo={'/edit'}
analysisAltText={'Analyze another Game'}>
<Heading text={'Analysis'} />
<Player slot={'local'} remote={false} analysisOnly={true} analysisDepth={ANALYSIS_DEPTH} />
</PageWithNavigationBar>;
const accountPage =
<PageWithNavigationBar>
<Heading text={'Account'} />
<Account listTo={'/remote/gameMenu'} />
</PageWithNavigationBar>;
const remoteGameMenu =
<PageWithNavigationBar remoteTo={undefined}>
<GameMenu
slot={'remote'}
gameIdentifier={GAME.identifier}
dragons={DRAGON_COUNT}
remote={true}
accountTo={'/remote/account'}
acceptTo={'/remote/invitation'}
newGameTo={'/remote/settingsMenu'}
loadGameTo={'/remote'} />
</PageWithNavigationBar>;
const invitationLoginPage =
<PageWithNavigationBar>
<Heading text={'Account'} />
<Account listTo={'/remote/invitation'} />
</PageWithNavigationBar>;
const invitationAcceptancePage =
<PageWithNavigationBar>
<Heading text={'Accept Invitation'} />
<Invitation slot={'remote'} to={'/remote'} backTo={'/remote/gameMenu'} />
</PageWithNavigationBar>;
const remoteSettingsMenu =
<PageWithNavigationBar>
<Heading text={'Choose Settings'} />
<SettingsMenu slot={'remote'} remote={true} backTo={'/remote/gameMenu'} to={'/remote'} />
</PageWithNavigationBar>;
const remoteBoard =
<PageWithNavigationBar>
<Heading text={'Network Game'} />
<Player
slot={'remote'}
remote={true}
analysisOnly={false}
analysisDepth={ANALYSIS_DEPTH}
backTo={'/remote/gameMenu'} />
<OneGameMonitor slot={'remote'} />
</PageWithNavigationBar>;
const notification =
<PageWithNavigationBar>
<NotificationHandler slot={'remote'} backTo={'/'} accountTo={'/notificationLogin'} to={'/remote'} />
</PageWithNavigationBar>;
const notificationLoginPage =
<PageWithNavigationBar>
<Heading text={'Account'} />
<Account listTo={'/remote'} />
</PageWithNavigationBar>;
return (
<>
<Routes>
<Route path={'/'} element={home} />
<Route path={'/rules/*'} element={rulesTutorial} />
<Route path={'/local/gameMenu/*'} element={localGameMenu} />
<Route path={'/local/settingsMenu/*'} element={localSettingsMenu} />
<Route path={'/local/*'} element={localBoard} />
<Route path={'/edit/gameMenu/*'} element={editGameMenu} />
<Route path={'/edit/*'} element={editBoard} />
<Route path={'/analysis/gameMenu/*'} element={analyzeGameMenu} />
<Route path={'/analysis/*'} element={analyzeBoard} />
<Route path={'/remote/account/*'} element={accountPage} />
<Route path={'/remote/gameMenu/*'} element={remoteGameMenu} />
<Route path={'/remote/invitationLogin/*'} element={invitationLoginPage} />
<Route path={'/remote/invitation/*'} element={invitationAcceptancePage} />
<Route path={'/remote/settingsMenu/*'} element={remoteSettingsMenu} />
<Route path={'/remote/*'} element={remoteBoard} />
<Route path={'/notification/*'} element={notification} />
<Route path={'/notificationLogin/*'} element={notificationLoginPage} />
</Routes>
<AbruptShutdownRecovery />
<Network slot={'remote'} />
</>
);
}
if (navigator.serviceWorker) {
console.log('Listening to service worker messages…');
navigator.serviceWorker.addEventListener('message', (message) => {
if (typeof message.data.hash === 'string') {
globalThis.location.hash = message.data.hash;
}
});
}