23 Commits

Author SHA1 Message Date
Sysy's
65bd05d47c Add laser overlay to not erase button etc 2026-03-31 10:59:52 +02:00
Sysy's
e90a1fc497 Add glass palette, colored lasers & UI layout
Introduce tinted glass mechanic and colored lasers with drag-and-drop palette, plus UI layout and styling. CSS: new game-layout, toolbox, glass-palette, glass-item, cell-glass, laser color classes and many visual tweaks (user-select, drop outline, door/button states). JS: add laserColors, glassOptions, glassPlacements, palette creation, drag/drop/dblclick handlers, block browser drop, saveLaserSegment helper, colored laser tracing (red/blue/yellow/white) including mirror/door/button interactions, button/door grouping and initial mirror angles, and updates to loadGrid to render glass and colorized laser segments. HTML: move map into new main layout and add toolbox palette container. Overall enables placing colored glass to influence laser behavior and updates visuals/interaction accordingly.
2026-03-31 10:53:18 +02:00
Sysy's
75a68a7c75 Add ads to the main game page 2026-03-31 10:27:44 +02:00
Sysy's
731d040e89 Remove index & change walls colors 2026-03-31 09:52:51 +02:00
Sysy's
5620fade9c Prevent rotation after level ending 2026-03-31 09:50:07 +02:00
Sysy's
09d54aa525 Add diagonal lasers 2026-03-31 09:46:29 +02:00
Sysy's
76de2a5a5c Remove player + right click change rotation mirror 2026-03-31 09:40:25 +02:00
Sysy's
284e396d5f Make mirror rotate on click 2026-03-31 09:32:59 +02:00
Sysy's
4ba42f6566 Track laser segments and update UI theme
Add per-cell laserSegments tracking and use it to render correct laser orientation: introduce laserSegments global, reset it at trace start, populate entries when tracing, and consult it in loadGrid to choose horizontal vs vertical classes. Update UI styling from a dark to a lighter theme (body and main backgrounds, cell/empty/mirror/wall colors and laser gradients), remove some borders/fit-content sizing, and add a "map" class to the map container in the HTML. These changes fix laser orientation rendering and refresh the game's visual theme.
2026-03-31 09:20:28 +02:00
M1n-0
d0e282b41c add .gitignore 2026-03-31 09:17:02 +02:00
024b80d393 Upgrade of design of light of laser + add posibility to move a cursor and rotate mirror with keyboard 2026-03-30 22:06:44 +02:00
34fbb797c1 Laser print and reflect 2026-03-30 16:48:53 +02:00
b10b6475d8 First version of button for mirrors 2026-03-30 15:33:49 +02:00
2dbfff770f Grid enlargement 2026-03-30 15:29:25 +02:00
a791415bd3 Rebase of feature/mirror + merge with the grid program 2026-03-30 15:05:57 +02:00
Sysy's
577357b090 Add rotateMirror to rotate element 45°
Add a small utility function in web/assets/js/index.js that increments an element's CSS rotation by 45 degrees. The function reads the element's inline transform (handling an empty value), parses the current rotation angle modulo 360, and sets the new rotate(angle+45) value.
2026-03-30 14:54:43 +02:00
1be3750672 Change index.js and index.css to game.js and game.css 2026-03-30 14:51:37 +02:00
5668d21b0d End of grid 2026-03-30 14:16:30 +02:00
001adb89bd First version of laser 2026-03-30 14:10:19 +02:00
04a0e1a912 Print grid on html + minimalist css (which will not be used) 2026-03-30 12:22:56 +02:00
e3747fd5a4 update read.me 2026-03-30 11:02:30 +02:00
7b0cd8c728 Merge branch 'main' of https://git.ninolbt.com/Nono/Projet_48h 2026-03-30 10:46:29 +02:00
dc064dd8f3 add object file 2026-03-30 10:46:09 +02:00
14 changed files with 919 additions and 799 deletions

1
.gitignore vendored
View File

@@ -0,0 +1 @@
Test

View File

@@ -1,20 +1,23 @@
règles : Règles :
un rayon laser constant Un rayon laser constant
un ou plusieurs spawn Un ou plusieurs laser
laser non movibles, on/off Laser non movibles, on/off
mirroirs orientables Mirroirs orientables
prisme Prisme (dédouble le laser ou le renvoi selon l'angle)
vitre colorée (rjb) Vitre colorée fixe ou placable par le joueur (r,j,b)
bouton allumable par laser spécifique (interaction porte, mirroir, etc) Bouton allumable par clique souris (interaction porte, mirroir, etc)
Bouton allumable par laser spécifique (interaction porte, mirroir, etc)
rayon : Rayon :
rouge - allumage bouton Blanc -> rebondis seulement sur les mirroirs et s'arrête contre les murs
bleu - base Rouge -> Allumage boutons
jaune - traverse tout Bleu -> Rebondis sur toutes les surfaces
Jaune -> Traverse tout (mirroir compris)
systeme de placement d'objet par grille Systeme de placement d'objet par grille
Comptes utilisateur
optionnel : timer, tableau de score, Optionnel : timer, tableau de score,

2
backend/.gitignore vendored
View File

@@ -1 +1 @@
/target /target

759
backend/Cargo.lock generated
View File

@@ -1,759 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"bytes",
"form_urlencoded",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-server"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1df331683d982a0b9492b38127151e6453639cd34926eb9c07d4cd8c6d22bfc"
dependencies = [
"bytes",
"either",
"fs-err",
"http",
"http-body",
"hyper",
"hyper-util",
"tokio",
"tower-service",
]
[[package]]
name = "backend"
version = "0.1.0"
dependencies = [
"axum",
"axum-server",
"serde",
"tokio",
"tracing-subscriber",
]
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "fs-err"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fde052dbfc920003cfd2c8e2c6e6d4cc7c1091538c3a24226cec0665ab08c0"
dependencies = [
"autocfg",
"tokio",
]
[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "h2"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
"atomic-waker",
"bytes",
"futures-channel",
"futures-core",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
"smallvec",
"tokio",
]
[[package]]
name = "hyper-util"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [
"bytes",
"http",
"http-body",
"hyper",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mio"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [
"itoa",
"serde",
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "tokio"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -1,11 +0,0 @@
[package]
name = "backend"
version = "0.1.0"
edition = "2024"
[dependencies]
axum = "0.8.8"
axum-server = "0.8.0"
serde = "1.0.228"
tokio = { version = "1.50.0", features = ["full"] }
tracing-subscriber = "0.3.23"

14
backend/object.rs Normal file
View File

@@ -0,0 +1,14 @@
struct Laser {
color : array,
}
struct Mirror {
color : array,
x : int,
y : int,
}
struct Spawn {
x : int,
y : int,
}

View File

@@ -1,14 +0,0 @@
use axum::{routing::get, Router};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, world!" }));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on {}", addr);
axum_server::bind(addr)
.serve(app.into_make_service())
.await
.unwrap();
}

297
web/assets/css/game.css Normal file
View File

@@ -0,0 +1,297 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100vw;
height: 100vh;
overflow: hidden;
}
body {
background: #f7f7f7;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Arial', sans-serif;
user-select: none;
}
main {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 10px;
min-width: fit-content;
flex-shrink: 0;
gap: 16px;
}
.game-layout {
width: min(95vw, 1000px);
}
.toolbox {
width: 100%;
background: #dfe5f8;
border-radius: 10px;
padding: 14px;
display: flex;
flex-direction: column;
gap: 10px;
}
.toolbox h2 {
font-size: 1rem;
}
.toolbox p {
font-size: 0.9rem;
color: #334;
}
.glass-palette {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
/* ================================
GRID
================================ */
.map {
display: flex;
flex-direction: column;
gap: 0px;
padding: 10px;
background: #DADEEF;
border-radius: 5px;
}
.lign {
display: flex;
gap: 0px;
margin: 0;
}
/* ================================
CELLS
================================ */
.cell {
width: clamp(28px, 5.5vmin, 60px);
height: clamp(28px, 5.5vmin, 60px);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
margin: 0;
position: relative;
background-color: #2a2a2a;
user-select: none;
overflow: hidden;
}
.cell.can-drop {
outline: 2px dashed rgba(0, 0, 0, 0.2);
}
/* ================================
CASES TYPE
================================ */
.empty {
background-color: #DADEEF;
}
.empty:hover {
background-color: #333333;
}
.laser {
background-color: #f5f5f5;
border: 2px solid #d8d8d8;
}
.colored-laser {
background-color: #ffa726;
}
.mirror {
background-color: #DADEEF;
border-color: #444444;
position: relative;
overflow: hidden;
}
.wall {
background-color: #0729c0;
}
.door {
background-color: #6d4c41;
}
.door-open {
background-color: #bca89c;
}
.button {
background-color: #7cb342;
}
.button-2 {
background-color: #ff8f00;
}
.button-active {
background-color: #c6ff00;
}
.target {
background: #00FF00;
}
/* ================================
LIGHT LASER
================================ */
.light-laser {
position: relative;
}
.laser-overlay {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 2;
}
.laser-horizontal {
--laser-color: red;
background: linear-gradient(to bottom, transparent 0%, transparent 45%, var(--laser-color) 45%, var(--laser-color) 55%, transparent 55%, transparent 100%);
}
.laser-vertical {
--laser-color: red;
background: linear-gradient(to right, transparent 0%, transparent 45%, var(--laser-color) 45%, var(--laser-color) 55%, transparent 55%, transparent 100%);
}
.laser-diagonal-down {
--laser-color: red;
background: linear-gradient(45deg, transparent 0%, transparent 46%, var(--laser-color) 46%, var(--laser-color) 54%, transparent 54%, transparent 100%);
}
.laser-diagonal-up {
--laser-color: red;
background: linear-gradient(135deg, transparent 0%, transparent 46%, var(--laser-color) 46%, var(--laser-color) 54%, transparent 54%, transparent 100%);
}
.laser-color-white {
--laser-color: #f8f8f8;
}
.laser-color-red {
--laser-color: #ff3b30;
}
.laser-color-blue {
--laser-color: #2d7ff9;
}
.laser-color-yellow {
--laser-color: #ffd400;
}
/* ================================
MIRROR
================================ */
.btn-mirror {
background: none;
border: none;
cursor: pointer;
height: 100%;
position: relative;
z-index: 3;
}
.btn-mirror::after {
content: '';
position: absolute;
top: 50%;
left: 10%;
width: 80%;
height: 2px;
background-color: #aaaaaa;
transform-origin: center;
}
.glass-item {
width: 54px;
height: 54px;
border: none;
border-radius: 10px;
cursor: grab;
position: relative;
box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.1);
user-select: none;
}
.glass-item::after,
.cell-glass::after {
content: '';
position: absolute;
inset: 10px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.45);
border: 1px solid rgba(255, 255, 255, 0.8);
}
.glass-red {
background: rgba(255, 59, 48, 0.85);
}
.glass-blue {
background: rgba(45, 127, 249, 0.85);
}
.glass-yellow {
background: rgba(255, 212, 0, 0.9);
}
.cell-glass {
position: absolute;
inset: 5px;
border-radius: 8px;
opacity: 0.9;
pointer-events: none;
z-index: 4;
}
/* ================================
RESPONSIVE
================================ */
@media (max-width: 600px) {
.map {
padding: 5px;
border-radius: 3px;
}
main {
padding: 8px;
}
}
@media (max-height: 500px) {
.map {
padding: 4px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

555
web/assets/js/game.js Normal file
View File

@@ -0,0 +1,555 @@
// Legend of grid case
const legend = {
empty: 0,
laser: 1,
coloredLaser: 2,
mirror: 3,
door: 4,
button: 5,
wall: 6,
demiWall: 7,
target: 8,
ligthLaser: 9,
button2: 10,
}
const laserColors = {
white: "white",
red: "red",
blue: "blue",
yellow: "yellow",
};
const glassOptions = [
laserColors.red,
laserColors.blue,
laserColors.yellow,
];
// Grid test
let level1 = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 4, 0, 0, 3, 0, 7, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 3, 5, 3, 0, 8, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
const initialMirrorAngles = {
"8,4": 315,
"2,4": 45,
"2,8": 315,
"6,8": 45,
};
const buttonGroups = {
"4,4": 1,
"8,5": 2,
};
const doorGroups = {
"2,5": 1,
"2,6": 2,
};
let laserDirection = { dx: 0, dy: 0 };
let laserSegments = {};
let mirrorOrientations = {};
let glassPlacements = {};
let activatedButtons = {};
let openedDoors = {};
function normalizeLaserDirection(dx, dy) {
const epsilon = 0.0001;
const normalizedDx = Math.abs(dx) < epsilon ? 0 : Math.sign(dx);
const normalizedDy = Math.abs(dy) < epsilon ? 0 : Math.sign(dy);
return { dx: normalizedDx, dy: normalizedDy };
}
function reflectLaser(direction, mirrorAngle) {
const mirrorRadians = mirrorAngle * (Math.PI / 180);
const mirrorVectorX = Math.cos(mirrorRadians);
const mirrorVectorY = Math.sin(mirrorRadians);
const dotProduct = (direction.dx * mirrorVectorX) + (direction.dy * mirrorVectorY);
const reflectedDx = (2 * dotProduct * mirrorVectorX) - direction.dx;
const reflectedDy = (2 * dotProduct * mirrorVectorY) - direction.dy;
return normalizeLaserDirection(reflectedDx, reflectedDy);
}
function getLaserSegmentClass(segmentDirection) {
if (!segmentDirection) {
return "laser-horizontal";
}
if (segmentDirection.dx === 0) {
return "laser-vertical";
}
if (segmentDirection.dy === 0) {
return "laser-horizontal";
}
if (segmentDirection.dx === segmentDirection.dy) {
return "laser-diagonal-down";
}
return "laser-diagonal-up";
}
function getLaserColorClass(color) {
return `laser-color-${color || laserColors.white}`;
}
function reverseLaser(direction) {
return {
dx: direction.dx * -1,
dy: direction.dy * -1,
};
}
function getButtonGroup(x, y) {
const cellType = level1[y][x];
if (cellType === legend.button2) {
return 2;
}
return buttonGroups[`${y},${x}`] || 1;
}
function getDoorGroup(x, y) {
return doorGroups[`${y},${x}`] || 1;
}
function openDoorsFromButton(x, y) {
const buttonGroup = getButtonGroup(x, y);
for (let doorY = 0; doorY < level1.length; doorY++) {
for (let doorX = 0; doorX < level1[doorY].length; doorX++) {
if (level1[doorY][doorX] === legend.door && getDoorGroup(doorX, doorY) === buttonGroup) {
openedDoors[`${doorY},${doorX}`] = true;
}
}
}
}
function saveLaserSegment(x, y, direction, color) {
laserSegments[`${y},${x}`] = {
direction: { ...direction },
color: color,
};
if (level1[y][x] === legend.empty) {
level1[y][x] = legend.ligthLaser;
}
}
function isGlassOnCell(x, y) {
return glassPlacements[`${y},${x}`] !== undefined;
}
function drawGlassInCell(cell, x, y) {
const glassColor = glassPlacements[`${y},${x}`];
if (!glassColor) {
return;
}
const glassDiv = document.createElement("div");
glassDiv.classList.add("cell-glass", `glass-${glassColor}`);
cell.appendChild(glassDiv);
}
function createPalette() {
const palette = document.getElementById("glass-palette");
if (!palette) {
return;
}
palette.innerHTML = "";
for (let i = 0; i < glassOptions.length; i++) {
const glassColor = glassOptions[i];
const glassButton = document.createElement("button");
glassButton.type = "button";
glassButton.classList.add("glass-item", `glass-${glassColor}`);
glassButton.draggable = true;
glassButton.addEventListener("dragstart", (event) => {
event.dataTransfer.effectAllowed = "copy";
event.dataTransfer.setData("text/plain", glassColor);
event.dataTransfer.setData("application/x-glass-color", glassColor);
});
palette.appendChild(glassButton);
}
}
function addDropEvents(cell, x, y) {
cell.addEventListener("dragover", (event) => {
if (isLevelFinished) {
return;
}
if (!event.dataTransfer.types.includes("application/x-glass-color")) {
return;
}
if (level1[y][x] !== legend.empty && level1[y][x] !== legend.ligthLaser) {
return;
}
event.preventDefault();
cell.classList.add("can-drop");
});
cell.addEventListener("dragleave", () => {
cell.classList.remove("can-drop");
});
cell.addEventListener("drop", (event) => {
if (isLevelFinished) {
return;
}
const selectedColor = event.dataTransfer.getData("application/x-glass-color");
cell.classList.remove("can-drop");
if (!selectedColor) {
return;
}
if (level1[y][x] !== legend.empty && level1[y][x] !== legend.ligthLaser) {
return;
}
event.preventDefault();
glassPlacements[`${y},${x}`] = selectedColor;
traceLaser();
});
cell.addEventListener("dblclick", () => {
if (isLevelFinished) {
return;
}
if (isGlassOnCell(x, y)) {
delete glassPlacements[`${y},${x}`];
traceLaser();
}
});
}
function blockBrowserDrop() {
document.addEventListener("dragover", (event) => {
event.preventDefault();
});
document.addEventListener("drop", (event) => {
event.preventDefault();
});
}
function initializeMirrorOrientations() {
mirrorOrientations = {}; // Reset
for (let y = 0; y < level1.length; y++) {
for (let x = 0; x < level1[y].length; x++) {
if (level1[y][x] === legend.mirror) {
mirrorOrientations[`${y},${x}`] = initialMirrorAngles[`${y},${x}`] || 0;
}
}
}
}
// Function to print grid
let mirrorCoordinates = [];
function loadGrid() {
const mapDiv = document.getElementById("map"); // Div with map in DOM
mapDiv.innerHTML = "";
for (let y = 0; y < level1.length; y++) {
const lign = document.createElement("div");
lign.classList.add("lign");
for (let x = 0; x < level1[y].length; x++) {
const cell = document.createElement("div");
cell.classList.add("cell");
addDropEvents(cell, x, y);
switch (level1[y][x]) {
case legend.empty:
cell.classList.add("empty");
break;
case legend.laser:
cell.classList.add("laser");
break;
case legend.coloredLaser:
cell.classList.add("colored-laser");
break;
case legend.mirror:
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
const btnMirror = document.createElement("button");
btnMirror.classList.add("btn-mirror");
btnMirror.type = "button";
btnMirror.style.transform = `rotate(${currentAngle}deg)`;
btnMirror.style.width = "100%";
btnMirror.onmousedown = (e) => {
e.preventDefault();
e.stopPropagation();
if(!isLevelFinished){
rotateMirror(x, y, e.button === 2);
}
};
btnMirror.oncontextmenu = (e) => e.preventDefault();
cell.appendChild(btnMirror);
cell.classList.add("mirror");
break;
case legend.door:
cell.classList.add("door");
if (openedDoors[`${y},${x}`]) {
cell.classList.add("door-open");
}
break;
case legend.button:
cell.classList.add("button");
if (activatedButtons[`${y},${x}`]) {
cell.classList.add("button-active");
}
break;
case legend.button2:
cell.classList.add("button", "button-2");
if (activatedButtons[`${y},${x}`]) {
cell.classList.add("button-active");
}
break;
case legend.wall:
cell.classList.add("wall");
break;
case legend.demiWall:
cell.classList.add("demi-wall");
break;
case legend.target:
cell.classList.add("target");
break;
case legend.ligthLaser:
cell.classList.add("empty");
break;
}
const segmentData = laserSegments[`${y},${x}`];
if (segmentData) {
const laserDiv = document.createElement("div");
laserDiv.classList.add("laser-overlay");
laserDiv.classList.add(getLaserSegmentClass(segmentData.direction));
laserDiv.classList.add(getLaserColorClass(segmentData.color));
cell.appendChild(laserDiv);
}
drawGlassInCell(cell, x, y);
lign.appendChild(cell);
}
mapDiv.appendChild(lign);
}
}
loadGrid();
// Function to rotate mirror
function rotateMirror(x, y, isRightClick) {
const coordKey = `${y},${x}`;
if (level1[y][x] !== legend.mirror) {
return;
}
let currentAngle = mirrorOrientations[coordKey] || 0;
// Rotation and normalize negative angles to [0, 360)
currentAngle = (currentAngle + (isRightClick ? 22.5 : -22.5)) % 360;
if (currentAngle < 0) {
currentAngle += 360;
}
// Save
mirrorOrientations[coordKey] = currentAngle;
// Print laser light
traceLaser(true);
}
// Function to trace
let isLevelFinished = false;
function traceLaser() {
// Reset light laser from previous trace
laserSegments = {};
activatedButtons = {};
openedDoors = {};
for (let y = 0; y < level1.length; y++) {
for (let x = 0; x < level1[y].length; x++) {
if (level1[y][x] === legend.ligthLaser) {
level1[y][x] = legend.empty;
}
}
}
let startLaserX;
let startLaserY;
// Search laser
for (let y = 0; y < level1.length; y++) {
for (let x = 0; x < level1[y].length; x++) {
if (level1[y][x] === legend.laser) {
startLaserX = x;
startLaserY = y;
laserDirection = { dx: 1, dy: 0 };
break;
}
}
if (startLaserX !== undefined) break;
}
// If laser not found -> return
if (startLaserX === undefined) {
return;
}
let currentX = startLaserX;
let currentY = startLaserY;
let laserActive = true;
let currentLaserColor = laserColors.white;
const maxIterations = 1000; // Prevent infinite loops
let iterations = 0;
isLevelFinished = false;
while (laserActive && iterations < maxIterations) {
iterations++;
currentX += laserDirection.dx;
currentY += laserDirection.dy;
// Out of bounds
if (currentX < 0 || currentX >= level1[0].length || currentY < 0 || currentY >= level1.length) {
laserActive = false;
break;
}
const cellType = level1[currentY][currentX];
const glassColor = glassPlacements[`${currentY},${currentX}`];
if (glassColor) {
currentLaserColor = glassColor;
}
switch (cellType) {
case legend.laser:
case legend.coloredLaser:
laserActive = false;
break;
case legend.empty:
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
break;
case legend.target:
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserActive = false;
isLevelFinished = true;
break;
case legend.mirror:
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
const mirrorAngle = mirrorOrientations[`${currentY},${currentX}`] || 0;
laserDirection = reflectLaser(laserDirection, mirrorAngle);
}
break;
case legend.wall:
if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.demiWall:
if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.door:
if (openedDoors[`${currentY},${currentX}`]) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.button:
case legend.button2:
if (currentLaserColor === laserColors.red) {
activatedButtons[`${currentY},${currentX}`] = true;
openDoorsFromButton(currentX, currentY);
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else {
laserActive = false;
}
break;
default:
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
break;
}
}
loadGrid();
if (isLevelFinished) {
finish();
}
}
createPalette();
initializeMirrorOrientations();
blockBrowserDrop();
traceLaser();
// If level finishh -> call this function
function finish() {
setTimeout(() => {
alert("Réussi !");
}, 100);
}

View File

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../assets/css/game.css">
<title>Game</title>
<script>(function(s){s.dataset.zone='10809858',s.src='https://n6wxm.com/vignette.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
<script>(function(s){s.dataset.zone='10809853',s.src='https://nap5k.com/tag.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
</head>
<body>
<script>
atOptions = {
'key' : '72b6ba1a1c26b9671167b66063c7e699',
'format' : 'iframe',
'height' : 600,
'width' : 160,
'params' : {}
};
</script>
<script src="https://www.highperformanceformat.com/72b6ba1a1c26b9671167b66063c7e699/invoke.js"></script>
<main class="game-layout">
<div id="map" class="map"></div>
<section class="toolbox">
<h2>Vitres tintées</h2>
<div id="glass-palette" class="glass-palette"></div>
</section>
</main>
<script src="../../assets/js/game.js" defer></script>
</body>
</html>