Refactoring

This commit is contained in:
aronmal 2022-08-15 23:16:31 +02:00
parent 83f187a7dc
commit 2ebaccfb2d
Signed by: aronmal
GPG key ID: 816B7707426FC612
9 changed files with 231 additions and 118 deletions

View file

@ -1,109 +1,41 @@
import { faBurst, faCrosshairs, faXmark } from '@fortawesome/free-solid-svg-icons';
import { faCrosshairs } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CSSProperties, useEffect, useState } from 'react';
import { TargetPreviewType } from './interfaces';
import { CSSProperties, useEffect, useReducer, useState } from 'react';
import BorderTiles from './components/BorderTiles';
import FogImages from './components/FogImages';
import HitElems from './components/HitElems';
import Labeling from './components/Labeling';
import Ships from './components/Ships';
import { hitReducer, initlialTarget, initlialTargetPreview } from './helpers';
import { HitType, TargetPreviewType, TargetType } from './interfaces';
import './styles/App.scss';
function App() {
const [target, setTarget] = useState({
show: false,
x: 0,
y: 0
})
const [targetPreview, setTargetPreview] = useState<TargetPreviewType>({
newX: 0,
newY: 0,
shouldShow: false,
appearOK: false,
eventReady: true,
show: false,
x: 0,
y: 0
})
const count = 12;
const [target, setTarget] = useState<TargetType>(initlialTarget);
const [targetPreview, setTargetPreview] = useState<TargetPreviewType>(initlialTargetPreview);
const [hits, DispatchHits] = useReducer(hitReducer, [] as HitType[]);
const [x, setX] = useState(0);
const [y, setY] = useState(0);
let borderTiles: JSX.Element[] = [];
let shipElems: JSX.Element[] = [];
let elems2: {
field: string,
x: number,
y: number,
}[] = [];
let hitElems: {
field: string,
x: number,
y: number,
}[] = [],
count = 12;
for (let y = 0; y < count; y++) {
elems2.push(...[
// Up
{ field: String.fromCharCode(65+y), x: y+2, y: 1 },
// Left
{ field: (y+1).toString(), x: 1, y: y+2 },
// Bottom
{ field: String.fromCharCode(65+y), x: y+2, y: count+2 },
// Right
{ field: (y+1).toString(), x: count+2, y: y+2 }
]);
for (let x = 0; x < count; x++) {
hitElems.push({ field: String.fromCharCode(65+x)+(y), x: x+2, y: y+2 })
useEffect(() => {
if (hits.length === count*count)
return;
if (x === count) {
setX(0);
setY(y => y+1);
return;
}
}
const hitSVGs = hitElems.map((obj, i) =>
<div key={i} className='hit-svg' style={{'--x': obj.x, '--y': obj.y} as CSSProperties}>
<FontAwesomeIcon icon={faBurst} />
</div>);
const corner = (x: number, y: number, count: number) => {switch (true) {
case x === 0 && y === 0:
return 'left-top-corner';
case x === count+1 && y === 0:
return 'right-top-corner';
case x === 0 && y === count+1:
return 'left-bottom-corner';
case x === count+1 && y === count+1:
return 'right-bottom-corner';
const autoTimer = setTimeout(() => {
DispatchHits({type: 'fireMissle', payload: {hit: (x+y)%2 !== 0, x: x+2, y: y+2}});
setX(x => x+1);
}, 100);
default:
return '';
}};
const border = (x: number, y: number, count: number) => {switch (true) {
case x === 0:
return 'left';
case y === 0:
return 'top';
case x === count+1:
return 'right';
case y === count+1:
return 'bottom';
default:
return '';
}};
for (let y = 0; y < count+2; y++) {
for (let x = 0; x < count+2; x++) {
const cornerReslt = corner(x, y, count);
const borderType = cornerReslt ? cornerReslt : border(x, y, count);
const isGameTile = x > 0 && x < count+1 && y > 0 && y < count+1;
const classNames = ['border-tile'];
if (borderType)
classNames.push('edge', borderType);
if (isGameTile)
classNames.push('game-tile');
borderTiles.push(
<div
key={y*(count+2)+x}
className={classNames.join(' ')}
style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties}
onClick={() => {
if (isGameTile)
setTarget({ show: true, x, y });
}}
onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))}
></div>
)
return () => {
clearTimeout(autoTimer);
}
}
}, [x, y, hits.length]);
// handle visibility and position change of targetPreview
useEffect(() => {
@ -150,33 +82,23 @@ function App() {
}
}, [targetPreview.shouldShow, targetPreview.newX, targetPreview.newY]);
for (let i = 1; i <= 6; i++) {
shipElems.push(
<div key={i} className={`ship s${i}`} style={{'--x': i+3} as CSSProperties}>
<img src={`/svgs/${i}.svg`} alt={`${i}.svg`}/>
</div>);
}
return (
<div className="App">
<header className="App-header">
<div id="game-frame" style={{'--i': count} as CSSProperties}>
{/* Bordes */}
{ borderTiles }
<BorderTiles count={count} targets={{setTarget, setTargetPreview}} />
{/* Collumn lettes and row numbers */}
{elems2.map((obj, i) =>
<span key={i} className={`${obj.field} r1`} style={{'--x': obj.x, '--y': obj.y} as CSSProperties}>{obj.field}</span>
)}
{ hitSVGs }
<Labeling count={count} />
{/* Ships */}
{/* { shipElems } */}
{/* <Ships /> */}
<HitElems hits={hits} />
{/* Fog images */}
{/* <img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} /> */}
{/* <FogImages /> */}
<div className={`hit-svg target ${target.show ? 'show' : ''}`} style={{'--x': target.x+1, '--y': target.y+1} as CSSProperties}>
<FontAwesomeIcon icon={faCrosshairs} />
</div>
@ -184,9 +106,9 @@ function App() {
<FontAwesomeIcon icon={faCrosshairs} />
</div>
</div>
<p>
{/* <p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
</p> */}
<a
className="App-link"
href="https://www.freepik.com/free-vector/militaristic-ships-set-navy-ammunition-warship-submarine-nuclear-battleship-float-cruiser-trawler-gunboat-frigate-ferry_10704121.htm"

View file

@ -0,0 +1,45 @@
import { CSSProperties } from 'react';
import { borderCN, cornerCN, fieldIndex } from '../helpers';
import { TargetPreviewType, TargetType } from '../interfaces';
function BorderTiles({count, targets: {setTarget, setTargetPreview}}: {count: number, targets: {setTarget: React.Dispatch<React.SetStateAction<TargetType>>, setTargetPreview: React.Dispatch<React.SetStateAction<TargetPreviewType>>}}) {
let tilesProperties: {
key: number,
isGameTile: boolean,
classNameString: string,
x: number,
y: number
}[] = [];
for (let y = 0; y < count+2; y++) {
for (let x = 0; x < count+2; x++) {
const key = fieldIndex(count, x, y);
const cornerReslt = cornerCN(count, x, y);
const borderType = cornerReslt ? cornerReslt : borderCN(count, x, y);
const isGameTile = x > 0 && x < count+1 && y > 0 && y < count+1;
const classNames = ['border-tile'];
if (borderType)
classNames.push('edge', borderType);
if (isGameTile)
classNames.push('game-tile');
const classNameString = classNames.join(' ')
tilesProperties.push({key, classNameString, isGameTile, x, y})
}
}
return <>
{tilesProperties.map(({key, classNameString, isGameTile, x, y}) => {
return <div
key={key}
className={classNameString}
style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties}
onClick={() => {
if (isGameTile)
setTarget({ show: true, x, y });
}}
onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))}
></div>
})}
</>
}
export default BorderTiles;

View file

@ -0,0 +1,9 @@
function FogImages() {
return <>
<img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} />
</>
}
export default FogImages;

View file

@ -0,0 +1,17 @@
import { faBurst, faXmark } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CSSProperties } from 'react';
import { HitType } from '../interfaces';
function HitElems({hits}: {hits: HitType[]}) {
return <>
{hits.map(({hit, x, y}, i) =>
<div key={i} className='hit-svg' style={{'--x': x, '--y': y} as CSSProperties}>
<FontAwesomeIcon icon={hit ? faBurst : faXmark} />
</div>
)}
</>
}
export default HitElems;

View file

@ -0,0 +1,29 @@
import { CSSProperties } from 'react'
import { fieldIndex } from '../helpers';
import { FieldType } from '../interfaces';
function Labeling({count}: {count: number}) {
let elems: (FieldType & {
orientation: string
})[] = [];
for (let x = 0; x < count; x++) {
elems.push(
// Up
{field: String.fromCharCode(65+x), x: x+2, y: 1, orientation: 'up'},
// Left
{field: (x+1).toString(), x: 1, y: x+2, orientation: 'left'},
// Bottom
{field: String.fromCharCode(65+x), x: x+2, y: count+2, orientation: 'bottom'},
// Right
{field: (x+1).toString(), x: count+2, y: x+2, orientation: 'right'}
);
}
elems = elems.sort((a, b) => fieldIndex(count, a.x, a.y)-fieldIndex(count, b.x, b.y));
return <>
{elems.map(({field, x, y, orientation}, i) =>
<span key={i} className={`label ${orientation} ${field}`} style={{'--x': x, '--y': y} as CSSProperties}>{field}</span>
)}
</>
}
export default Labeling;

View file

@ -0,0 +1,19 @@
import { CSSProperties } from 'react'
function Ships() {
let shipIndexes: number[] = [];
for (let i = 1; i <= 6; i++) {
shipIndexes.push(i);
}
return <>
{shipIndexes.map(i =>
<div key={i} className={`ship s${i}`} style={{'--x': i+3} as CSSProperties}>
<img src={`/svgs/${i}.svg`} alt={`${i}.svg`}/>
</div>
)}
</>
}
export default Ships;

52
frontend/src/helpers.ts Normal file
View file

@ -0,0 +1,52 @@
import { HitType, HitDispatchType } from "./interfaces";
export const hitReducer = (formObject: HitType[], action: HitDispatchType) => {
switch (action.type) {
case 'fireMissle': {
const result = [...formObject, action.payload];
return result;
}
default:
return formObject;
}
}
export const fieldIndex = (count: number, x: number, y: number) => y*(count+2)+x;
export const cornerCN = (count: number, x: number, y: number) => {
if (x === 0 && y === 0)
return 'left-top-corner';
if (x === count+1 && y === 0)
return 'right-top-corner';
if (x === 0 && y === count+1)
return 'left-bottom-corner';
if (x === count+1 && y === count+1)
return 'right-bottom-corner';
return '';
};
export const borderCN = (count: number, x: number, y: number) => {
if (x === 0)
return 'left';
if (y === 0)
return 'top';
if (x === count+1)
return 'right';
if (y === count+1)
return 'bottom';
return '';
};
export const initlialTarget = {
show: false,
x: 0,
y: 0
};
export const initlialTargetPreview = {
newX: 0,
newY: 0,
shouldShow: false,
appearOK: false,
eventReady: true,
show: false,
x: 0,
y: 0
};

View file

@ -1,3 +1,8 @@
export interface TargetType {
show: boolean,
x: number,
y: number
};
export interface TargetPreviewType {
newX: number,
newY: number,
@ -7,4 +12,18 @@ export interface TargetPreviewType {
show: boolean,
x: number,
y: number
}
};
export interface FieldType {
field: string,
x: number,
y: number,
};
export interface HitType {
hit: boolean,
x: number,
y: number,
};
interface fireMissle { type: 'fireMissle', payload: { x: number, y: number, hit: boolean } };
export type HitDispatchType = fireMissle;

View file

@ -48,7 +48,7 @@
grid-template-columns: .75fr repeat(12, 1fr) .75fr;
// grid-gap: 1px solid blue;
> .r1 {
> .label {
grid-column: var(--x);
grid-row: var(--y);
}
@ -83,6 +83,7 @@
// justify-content: center;
border: 1px solid yellow;
grid-row: var(--x);
pointer-events: none;
img {
position: absolute;