Upd. PicoBlog, Minesweeper; Add. 2048; Fix. CSS

This commit is contained in:
octospacc 2023-03-26 18:05:22 +02:00
parent e4cdaf8e70
commit 3b296853ba
15 changed files with 797 additions and 6 deletions

View File

@ -1,3 +1,7 @@
#!/usr/bin/env node
require('../../Scripts/Lib/SelfBuild.js').importAll();
Fs.writeFileSync(__filename.split('.SelfBuild.js')[0], `
/* Global styles applied to all pages with any base template.
*
* Note to future self:
@ -10,7 +14,7 @@
.Inline { Display: Inline; }
.InlineBlock { Display: Inline-Block; }
.NoCol { Color: Transparent !Important; }
.NoDisplay, .DispNone { Display: None; }
.NoDisp, .NoDisplay, .DispNone { Display: None; }
.DispBlock { Display: Block; }
.NoWrap { White-Space: NoWrap; }
@ -29,10 +33,19 @@
}
/* Set Headings as Inline when inside Details Summaries and List Items */
:Where(Summary, Li) > :Where(H1, H2, H3, H4, H5, H6) {
/*:Where(Summary, Li) > :Where(H1, H2, H3, H4, H5, H6) {*/
${Where('summary >', CssAllHeadings, '')},
${Where('li >', CssAllHeadings, '')} {
Display: Inline;
}
/* Python-Markdown can put a <p> for list text items, forming spacing between it and sublists.
Wrapping a list in a div with this class when this effect is unwanted.
*/
.ListNoInMargin > * > li > p { margin-bottom: 0; }
/* Strange <br>s are sometimes formed */
/*.ListNoInMargin > * > li > p > br:last-of-type { display: none; }*/
/* Animazioni per le desinenze */
.BlinkA {
Animation: BlinkerA 0.25s Step-Start Infinite;
@ -50,3 +63,4 @@
50% {Position: Absolute; Visibility: Hidden;}
100% {Position: Static; Visibility: Visible;}
}
`);

View File

@ -0,0 +1,197 @@
// Multipurpose embeddable Minesweeper game on top of vuesweeper
// How many pixels one square takes up
var MineSquareSize = 32;
var Notices = {
NoticeLocked: "<i><big>Ops!</big> Questo contenuto è bloccato. Completa una partita a Minesweeper per accedervi.</i> <big>🙃️</big>",
AlertLockedWon: "Hai vinto! Goditi il contenuto sbloccato. 💖️",
AlertLockedLost: "Ops! Hai perso! Ritenta. 🙃️",
AlertLockedEgg: "Hai scoperto l'easter egg e hai saltato la partita. Mah. 🙄️",
AlertLockedResize: "Non si imbroglia ridimensionando la finestra!!! Il gioco verrà resettato. Non riceverai ulteriori avvisi. 😈️",
};
var ResizeAlerted = false;
// Add styles to current page
var New = document.createElement('style');
New.innerHTML = `
/* Don't know what's up with the percentages */
.Minesweeper {
width: calc(100% - 5% - 1.25%);
z-index: 4;
}
.Minesweeper > iframe {
max-height: none !important;
border: none;
}
.Minesweeper.Locker {
position: absolute;
}
`;
document.body.appendChild(New);
// Setup all Minesweeper boards on the page
document.querySelectorAll('.Minesweeper').forEach(function(Container){
// Prevent excessive flickering when page is still loading
Container.style.display = 'none';
var Frame = document.createElement('iframe');
Frame.src = '/vuesweeper-core/';
Container.appendChild(Frame);
var GameWindow = Frame.contentWindow;
var Game, GameState;
var DoContentUnlock;
function GetGameSecs(Game) {
var Vals = Game.state._value;
var Ms = `${Vals.endMS - Vals.startMS}`;
var Time = Math.round(`${Ms.slice(0, -3)}.${Ms.slice(-3)}`);
return Time;
};
window.onload = function(){
Container.style.display = '';
Game = GameWindow.vuesweeper;
var [Width, Height, Bombs] = [15, 15, 36];
function SetupLockGame() {
// Reset styles and remove locker elements
DoContentUnlock = function DoContentUnlock() {
LockedEl.style['margin-top'] = '';
LockedEl.style['margin-bottom'] = '';
NoticeEl.remove();
Container.remove();
};
// Element of content to lock must be an immediately succeding sibiling of the game container
var LockedEl = Container.nextElementSibling;
LockedEl.style.visibility = '';
// Add notice to the user saying to win a game to see the content underneath
// TODO: Maybe put it instead of the iframe, with a button, and when the user clicks that the iframe is set up?
var NoticeEl = document.createElement('p');
NoticeEl.innerHTML = Notices.NoticeLocked;
Container.before(NoticeEl);
var NoticeClicked = 0;
NoticeEl.onclick = function(){
NoticeClicked += 1;
if (NoticeClicked >= (Game.mines / 2)) {
alert(Notices.AlertLockedEgg);
DoContentUnlock();
};
};
// Set game window to size of content, adding needed paddings to window and content
var FromStyle = getComputedStyle(LockedEl);
var Margin = MineSquareSize * 2;
// LR padding to prevent content spilling
LockedEl.style['padding-left'] = `${~~FromStyle['padding-left'].split('px')[0] + 16}px`;
LockedEl.style['padding-right'] = `${~~FromStyle['padding-right'].split('px')[0] + 16}px`;
// Ensure game is tall enough and doesn't cover more than needed content
Frame.style.height = `${~~FromStyle.height.split('px')[0] + (MineSquareSize * 3.5)}px`;
LockedEl.style['margin-top'] = `${Margin}px`;
LockedEl.style['margin-bottom'] = `${Margin}px`;
// Set important color/transparency styles
var UnlockStyle = GameWindow.document.getElementById('GameStyle-Locker');
if (!UnlockStyle) {
UnlockStyle = document.createElement('style');
UnlockStyle.id = 'GameStyle-Locker';
UnlockStyle.innerHTML = `
html, body {
overflow: hidden;
}
button.bg-gray-500\\/10 {
background: #d0d0d0;
outline: 2px solid #e0e0e0;
}
button.bg-gray-500\\/10:hover {
background: rgba(192, 192, 192, 0.80);
}
button.bg-red-500\\/50 {
background-color: #e07070;
outline: 2px solid #e0e0e0;
}
button.text-transparent {
background: rgba(192, 192, 192, 0.75);
outline: 2px solid rgba(192, 192, 192, 0.75);
}
button.text-blue-500,
button.text-green-500,
button.text-yellow-500,
button.text-orange-500,
button.text-red-500,
button.text-purple-500,
button.text-pink-500,
button.text-teal-500 {
background: #e0e0e0;
outline: 2px solid #e0e0e0;
}
`;
GameWindow.document.body.appendChild(UnlockStyle);
};
// Set board size according to content size on screen
[Width, Height] = [
~~(FromStyle.width.split('px')[0] / MineSquareSize) - 1,
~~(FromStyle.height.split('px')[0] / MineSquareSize),
];
Bombs = ~~(2 * Math.sqrt(Width * Height));
// No cheating! (And no breaking my fragile CSS :c)
window.onresize = function(){
if (!ResizeAlerted) {
setTimeout(function(){
LockedEl.style.visibility = 'hidden';
alert(Notices.AlertLockedResize);
}, 150);
ResizeAlerted = true;
};
LockedEl.style['padding-left'] = '';
LockedEl.style['padding-right'] = '';
NoticeEl.remove();
SetupLockGame();
};
Game.reset(Width, Height, Bombs);
};
// Game board used to unlock page content on game win
if (Container.classList.contains('Locker')) {
SetupLockGame();
}
// Normal game board
else {
// TODO:
// Add buttons to select board size and control game execution
// Proper CSS with scrollbars always visible on screen when needed due to board overflow
};
Game.reset(Width, Height, Bombs);
};
// When user clicks the board, it's a good time for checking win/loss
GameWindow.onclick = function(){
var OldState = GameState;
var CurState = Game.state._value.status;
if (OldState != CurState) {
// On win, remove the board from the page
if (CurState == 'won') {
setTimeout(function(){
alert(Notices.AlertLockedWon + ` (${Game.width}x${Game.height}, ${Game.mines} mine, in ${GetGameSecs(Game)}s)`);
DoContentUnlock();
}, 1000);
} else
// On lose, reset the board
if (CurState == 'lost') {
setTimeout(function(){
alert(Notices.AlertLockedLost);
Game.reset();
}, 300);
};
GameState = CurState;
};
};
});

View File

@ -76,7 +76,7 @@ Details Div {
}
.BorderBox,
${Where('.BorderBoxContainer >', ' Div, Details', ':Not(.NoBorderBox)')}
Details Div Details:Not(.NoBorderBox) {
/*Details Div Details:Not(.NoBorderBox)*/ {
Border: 2px Solid Purple;
Margin: 8px;
Padding: 4px;

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-PRESENT Anthony Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,429 @@
// This file was manually hacked directly from the compiled version downloaded from <https://vuesweeper.netlify.app/>.
// Change AppDeployPath from "/vuesweeper-core/" to wherever you put this folder on the server.
// Unfortunately Vue won't work with relative paths, only absolute, and won't work served from file:///.
var AppDeployPath = "/vuesweeper-core/";
var C = Object.getOwnPropertySymbols;
var $ = (i, e, t) => e in i ? Object.defineProperty(i, e, {
enumerable: !0,
configurable: !0,
writable: !0,
value: t
}) : i[e] = t,
g = (i, e) => {
for (var t in e || (e = {})) Object.prototype.hasOwnProperty.call(e, t) && $(i, t, e[t]);
if (C)
for (var t of C(e)) Object.prototype.propertyIsEnumerable.call(e, t) && $(i, t, e[t]);
return i
},
p = (i, e) => Object.defineProperties(i, Object.getOwnPropertyDescriptors(e));
import { d as v, w as Z, o, c as u, a as _, u as q, b as E, r as S, e as f, F as x, t as b, f as H, n as P, g as z, h as y, i as K, j as W, k as d, l as w, m as G, p as k, q as J, s as Q, v as U, x as X, y as Y, z as ee } from "./vendor.js";
const te = function() {
const e = document.createElement("link").relList;
if (e && e.supports && e.supports("modulepreload")) return;
for (const n of document.querySelectorAll('link[rel="modulepreload"]')) r(n);
new MutationObserver(n => {
for (const s of n)
if (s.type === "childList")
for (const a of s.addedNodes) a.tagName === "LINK" && a.rel === "modulepreload" && r(a)
}).observe(document, {
childList: !0,
subtree: !0
});
function t(n) {
const s = {};
return n.integrity && (s.integrity = n.integrity), n.referrerpolicy && (s.referrerPolicy = n.referrerpolicy), n.crossorigin === "use-credentials" ? s.credentials = "include" : n.crossorigin === "anonymous" ? s.credentials = "omit" : s.credentials = "same-origin", s
}
function r(n) {
if (n.ep) return;
n.ep = !0;
const s = t(n);
fetch(n.href, s)
}
};
te();
const ne = v({
props: {
passed: {
type: Boolean
}
},
setup(i) {
const e = i;
function t() {
const r = {
colors: ["#5D8C7B", "#F2D091", "#F2A679", "#D9695F", "#8C4646"],
shapes: ["square"],
ticks: 500
};
_(p(g({}, r), {
particleCount: 80,
spread: 100,
origin: {
y: 0
}
})), setTimeout(() => {
_(p(g({}, r), {
particleCount: 50,
angle: 60,
spread: 80,
origin: {
x: 0
}
}))
}, 250), setTimeout(() => {
_(p(g({}, r), {
particleCount: 50,
angle: 120,
spread: 80,
origin: {
x: 1
}
}))
}, 400)
}
return Z(() => e.passed, r => {
r && setTimeout(t, 300)
}, {
flush: "post"
}), (r, n) => (o(), u("div"))
}
}),
j = q(),
se = E(j),
N = S(!1);
E(N);
const re = {
key: 0,
"i-mdi-flag": "",
"text-red": ""
},
ae = {
key: 0,
"i-mdi-mine": ""
},
ie = {
key: 1,
"font-600": ""
},
oe = v({
props: {
block: null
},
emits: ["lrclick"],
setup(i, {
emit: e
}) {
function t(s) {
s.buttons === 3 && e("lrclick", s)
}
const r = ["text-transparent", "text-blue-500", "text-green-500", "text-yellow-500", "text-orange-500", "text-red-500", "text-purple-500", "text-pink-500", "text-teal-500"];
function n(s) {
return s.flagged ? "bg-gray-500/10" : s.revealed ? s.mine ? "bg-red-500/50" : r[s.adjacentMines] : "bg-gray-500/10 hover:bg-gray-500/20"
}
return (s, a) => (o(), u("button", {
flex: "~",
"items-center": "",
"justify-center": "",
"min-w-8": "",
"min-h-8": "",
m: "1px",
border: "0.5 gray-400/10",
class: P(n(i.block)),
onMousedown: t
}, [i.block.flagged ? (o(), u("div", re)) : i.block.revealed || f(N) ? (o(), u(x, {
key: 1
}, [i.block.mine ? (o(), u("div", ae)) : (o(), u("div", ie, b(i.block.adjacentMines), 1))], 64)) : H("", !0)], 34))
}
}),
le = [
[1, 1],
[1, 0],
[1, -1],
[0, -1],
[-1, -1],
[-1, 0],
[-1, 1],
[0, 1]
];
class ue {
constructor(e, t, r) {
this.width = e, this.height = t, this.mines = r, this.state = S(), this.reset()
}
get board() {
return this.state.value.board
}
get blocks() {
return this.state.value.board.flat()
}
reset(e = this.width, t = this.height, r = this.mines) {
this.width = e, this.height = t, this.mines = r, this.state.value = {
mineGenerated: !1,
status: "ready",
board: Array.from({
length: this.height
}, (n, s) => Array.from({
length: this.width
}, (a, l) => ({
x: l,
y: s,
adjacentMines: 0,
revealed: !1
})))
}
}
randomRange(e, t) {
return Math.random() * (t - e) + e
}
randomInt(e, t) {
return Math.round(this.randomRange(e, t))
}
generateMines(e, t) {
const r = () => {
const n = this.randomInt(0, this.width - 1),
s = this.randomInt(0, this.height - 1),
a = e[s][n];
return Math.abs(t.x - a.x) <= 1 && Math.abs(t.y - a.y) <= 1 || a.mine ? !1 : (a.mine = !0, !0)
};
Array.from({
length: this.mines
}, () => null).forEach(() => {
let n = !1;
for (; !n;) n = r()
}), this.updateNumbers()
}
updateNumbers() {
this.board.forEach(e => {
e.forEach(t => {
t.mine || this.getSiblings(t).forEach(r => {
r.mine && (t.adjacentMines += 1)
})
})
})
}
expendZero(e) {
e.adjacentMines || this.getSiblings(e).forEach(t => {
t.revealed || (t.flagged || (t.revealed = !0), this.expendZero(t))
})
}
onRightClick(e) {
this.state.value.status === "play" && (e.revealed || (e.flagged = !e.flagged))
}
onClick(e) {
if (this.state.value.status === "ready" && (this.state.value.status = "play", this.state.value.startMS = +new Date), !(this.state.value.status !== "play" || e.flagged)) {
if (this.state.value.mineGenerated || (this.generateMines(this.board, e), this.state.value.mineGenerated = !0), e.revealed = !0, e.mine) {
this.onGameOver("lost");
return
}
this.expendZero(e)
}
}
getSiblings(e) {
return le.map(([t, r]) => {
const n = e.x + t,
s = e.y + r;
if (!(n < 0 || n >= this.width || s < 0 || s >= this.height)) return this.board[s][n]
}).filter(Boolean)
}
showAllMines() {
this.board.flat().forEach(e => {
e.mine && (e.revealed = !0)
})
}
checkGameState() {
if (!this.state.value.mineGenerated || this.state.value.status !== "play") return;
this.board.flat().some(t => !t.mine && !t.revealed) || this.onGameOver("won")
}
autoExpand(e) {
if (this.state.value.status !== "play" || e.flagged) return;
const t = this.getSiblings(e),
r = t.reduce((a, l) => a + (l.flagged ? 1 : 0), 0),
n = t.reduce((a, l) => a + (!l.revealed && !l.flagged ? 1 : 0), 0);
r === e.adjacentMines && t.forEach(a => {
a.revealed || a.flagged || (a.revealed = !0, this.expendZero(a), a.mine && this.onGameOver("lost"))
});
const s = e.adjacentMines - r;
n === s && t.forEach(a => {
!a.revealed && !a.flagged && (a.flagged = !0)
})
}
onGameOver(e) {
this.state.value.status = e, this.state.value.endMS = +Date.now(), e === "lost" && (this.showAllMines(), setTimeout(() => {
//alert("lost")
}, 10))
}
}
const ce = w(),
de = {
flex: "~ gap1",
"justify-center": "",
p4: ""
},
fe = {
flex: "~ gap-10",
"justify-center": ""
},
he = {
"font-mono": "",
"text-2xl": "",
flex: "~ gap-1",
"items-center": ""
},
me = d("div", {
"i-carbon-timer": ""
}, null, -1),
ge = {
"font-mono": "",
"text-2xl": "",
flex: "~ gap-1",
"items-center": ""
},
pe = d("div", {
"i-mdi-mine": ""
}, null, -1),
ve = {
p5: "",
"w-full": "",
"overflow-auto": ""
},
GameMain = v({
setup(i) {
const e = new ue(9, 9, 10),
t = z(),
r = y(() => {
var l, c;
return Math.round((((l = e.state.value.endMS) != null ? l : +t.value) - ((c = e.state.value.startMS) != null ? c : +t.value)) / 1e3)
});
// Export game object
window.vuesweeper = e;
// Actualy don't use localStorage, as saving the game state glitches after a while
//K("vuesweeper-state", e.state);
const n = y(() => e.board),
s = y(() => e.state.value.mineGenerated ? e.blocks.reduce((l, c) => l - (c.flagged ? 1 : 0), e.mines) : e.mines);
function a(l) {
switch (l) {
case "easy":
e.reset(9, 9, 10);
break;
case "medium":
e.reset(16, 16, 40);
break;
case "hard":
e.reset(16, 30, 99);
break;
}
}
return W(() => {
e.checkGameState()
}), (l, c) => {
const D = oe,
A = ne;
return o(), u("div", null, [ce, /*d("div", de, [
d("button", {
btn: "",
onClick: c[0] || (c[0] = h => f(e).reset())
}, " New Game "),
d("button", {
btn: "",
onClick: c[1] || (c[1] = h => a("easy"))
}, " Easy "),
d("button", {
btn: "",
onClick: c[2] || (c[2] = h => a("medium"))
}, " Medium "),
d("button", {
btn: "",
onClick: c[3] || (c[3] = h => a("hard"))
}, " Hard "),
]),*/ d("div", fe, [d("div", he, [me, w(" " + b(r.value), 1)]), d("div", ge, [pe, w(" " + b(s.value), 1)])]), d("div", ve, [(o(!0), u(x, null, G(n.value, (h, O) => (o(), u("div", {
key: O,
flex: "~",
"items-center": "",
"justify-center": "",
"w-max": "",
ma: ""
}, [(o(!0), u(x, null, G(h, (m, F) => (o(), J(D, {
key: F,
block: m,
onClick: M => f(e).onClick(m),
onLrclick: M => f(e).autoExpand(m),
onContextmenu: Q(M => f(e).onRightClick(m), ["prevent"])
}, null, 8, ["block", "onClick", "onLrclick", "onContextmenu"]))), 128))]))), 128))]), k(A, {
passed: f(e).state.value.status === "won"
}, null, 8, ["passed"])])
}
}
}),
VueRoutes = [{
name: "index",
path: "/",
component: GameMain,
props: !0
}, {
name: AppDeployPath,
path: AppDeployPath,
component: GameMain,
props: !0
}],
xe = {
"text-xl": "",
"mt-6": "",
"inline-flex": "",
"gap-2": ""
}//,
/*
be = {
key: 0,
"i-carbon-moon": ""
},
we = {
key: 1,
"i-carbon-sun": ""
},
*/
//Me = v({
// setup(i) {
// return (e, t) => (o(), u("nav", xe, [d("button", {
// class: "icon-btn !outline-none",
// onClick: t[0] || (t[0] = r => f(se)())
// }, /*[f(j) ? (o(), u("div", be)) : (o(), u("div", we))]*/)]))
// }
//});
var Ce = (i, e) => {
const t = i.__vccOpts || i;
for (const [r, n] of e) t[r] = n;
return t
};
const $e = {},
Ge = {
"font-sans": "",
p: "y-10",
text: "center gray-700 dark:gray-200"
};
function Ee(i, e) {
const t = U("router-view");
//const r = Me;
return o(), u("main", Ge, [k(t), /*k(r)*/])
}
var Se = Ce($e, [
["render", Ee]
]);
const AppRuntime = X(Se);
const RouteRuntime = Y({
history: ee(),
routes: VueRoutes
});
AppRuntime.use(RouteRuntime);
AppRuntime.mount("#app");

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<!-- This file was manually hacked directly from the compiled version downloaded from <https://vuesweeper.netlify.app/>. -->
<!-- Important: Edit app.js to change AppDeployPath from "/vuesweeper-core/" to wherever you put this folder on the server. -->
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script type="module" src="./vendor.js"></script>
<script type="module" src="./app.js"></script>
<link rel="stylesheet" href="./style.css"/>
<style>
html, body {
user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
main {
padding: 0 !important;
}
main > div > div:nth-child(2) {
padding-top: 1em !important;
padding-bottom: 1em !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
</style>
</head>
<body class="font-sans dark:text-white dark:bg-hex-121212" oncontextmenu="return false;">
<div id="app"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,6 +37,15 @@ href='./PicoBlog.html'>disponibile qui</a>. Considera di consultare quella per u
</div>
<details markdown="1" open><summary>
#### [2023-03-26] Ora solo chi ha skill può leggere. </summary>
Per fare qualcosa di epico, e contemporaneamente gradito ad alcune persone ma sgraditissimo ad altre, sulla mia pagina dedicata al **campo minato** ho fatto una roba: la sezione [curiosità](./Raccolte/Gaming/Minesweeper.html#-Curiosit) è bloccata, e per poterla leggere bisogna completare una partita al giochino. Non sai giocare? Peggio per te. 🙃️. Vabbe, non totalmente:
* Man mano che si premono i quadratini, questi diventano semi-trasparenti e fanno intravedere il testo sotto.
* Il gioco sparisce se si disattiva JavaScript, e la sezione di testo non viene coperta.
* Ho incluso un easter egg che fa saltare la partita senza doverla vincere. 👀️
</details>
<details markdown="1" open><summary>
#### [2023-03-23] Finalmente certe pagine... </summary>
Oggi ho finito di sistemare la grafica di alcune pagine che ho iniziato a creare ieri, [Minesweeper](./Raccolte/Gaming/Minesweeper.html) e [Nonogram](./Raccolte/Gaming/Nonogram.html). Quando avevo iniziato il sito quasi un anno fa pianificavo di fare cose di questo tipo in questo modo, ma non ho mai fatto molto fino ad ora. Vedrò di continuare così...

View File

@ -0,0 +1,50 @@
// % Categories = Gaming
// % EditedOn = 2023-03-26
<style>/*
*/#Body {
Color: #FFFFFF;
Background: #000000;
}
/*
*/#Background {
Background-Color: #000000;
Background-Image: url('[staticoso:CustomPath:Assets]/Media/2048/yandex.com-games-app-176908.jpg');
Filter: Blur(5px);
}
/**/#LeftBoxContainer, #RightBoxContainer { Color: #FFFFFF; }
/**/#MainBoxTop, #BuildTimeLine { Color: revert; }
h1, h2, h3, h4, h5, h6 { color: #e090e0; }
/*
*/#MainBox {
Background: RGBA(0, 0, 0, 0.80);
backdrop-filter: Blur(5px);
}
</style>
# 🔢️ 2048
**2048** è un semplice giochino puzzle in cui si ha una griglia di piastrelle numerate, generate casualmente, che si può far scorrere interamente in una delle quattro direzioni. Tutti i numeri sono potenze di 2, e numeri uguali che si scontrano vanno a sommarsi per diventare il loro doppio.
* Se si arriva, a furia di far sommare le piastrelle, a 2048, **si vince** una partita, che però può andare avanti anche oltre.
* **Si perde** se si arriva a riempire la griglia interamente di numeri senza avere spazio per fare altre mosse.
L'implementazione ufficiale del gioco è open source, cosa che ha portato alla creazione di tantissimi cloni e versioni modificate che implementano le meccaniche più svariate.
...**WIP**... Se vuoi scoprire altre cosine sul gioco, leggi su [Wikipedia](https://it.wikipedia.org/wiki/2048_(videogioco)){[:MdTgtBlank:]}.
## Implementazioni
* **Ufficiale** (Gabriele Cirulli): [Web](https://play2048.co/){[:MdTgtBlank:]}, [Git](https://github.com/gabrielecirulli/2048){[:MdTgtBlank:]}
## Cloni
* **2248**: [Web](https://2248game.com/){[:MdTgtBlank:]}
## [:HNotesRefsHtml:]
[^PageBg]: **Sfondo della Pagina**: [Fonte](https://yandex.com/games/app/176908){[:MdTgtBlank:]}

View File

@ -1,5 +1,5 @@
// % Categories = Gaming
// % EditedOn = 2023-03-22
// % EditedOn = 2023-03-26
<style>/*
*/#Body {
@ -35,12 +35,32 @@ Personalmente, chiamo il campo minato anche "**minaspazzante**", per scherzare s
## Curiosità
<div class="Minesweeper Locker"></div>
* Negli anni '90, erano tutti [fissati con Minesweeper a Microsoft, soprattutto Bill Gates](https://arstechnica.com/gaming/2023/02/how-bill-gates-minesweeper-addiction-helped-lead-to-the-xbox/){[:MdTgtBlank:]}. Direi che non è difficile vedere come...
* Nel 1999, a seguito di lamentele di un certo Sergio Chiodo (??), che argomentava come il concetto del gioco al campo minato fosse offensivo per le vittime di vere mine anti-uomo nel mondo, nasce in Italia la [Campagna Internazionale per la messa al Bando di Campo Minato](http://fc.retecivica.milano.it/rcmweb/fnm/princ.htm#italiano){[:MdTgtBlank:]}. Microsoft ha parzialmente ceduto alla pressione, e in Windows Me e 2000 ha sostituito le bombe nel gioco con dei fiori, e rinominato il gioco in "Prato fiorito".[^Regional_Differences]
* La campagna ha preso piede solo limitatamente fuori dall'Italia (?), e solo con Windows Vista il concetto di gioco al prato fiorito è diventato globale. Microsoft ha dato la possibilità di cambiare il gioco incluso nel sistema tra tema mine e tema fiori, impostazione che ha un diverso valore predefinito in base alla nazione.[^Windows_Vista_and_Windows_7]
## Implementazioni
<div class="ListNoInMargin" markdown="1">
* Per **PC**, l'**originale di Microsoft** non è malaccio. La versione per **Windows** XP funziona sulle ultime versioni del sistema, e persino su **Wine**. Purtroppo, scala un po' male su schermi ad alta risoluzione, in quanto i quadratini sono di una dimensione fissa e non esiste zoom. Da anni non è incluso in Windows, forse per questo motivo, ma si può scaricare dal Web:
* Versione XP Globale: [Minesweeper.exe](https://archive.org/download/Minesweeper_201811/Minesweeper.exe){[:MdTgtBlank:]}
* Versione XP Italiana: [Prato Fiorito.exe](https://archive.org/download/prato-fiorito/PRATO_FIORITO.exe){[:MdTgtBlank:]}
* Su **Android**, e azzardo a dire in generale, la migliore implementazione che ho trovato è **Antimine**.
* Scaricare da F-Droid: [dev.lucanlm.antimine](https://f-droid.org/packages/dev.lucanlm.antimine/){[:MdTgtBlank:]}
* Codice Sorgente: [antimine-android](https://github.com/lucasnlm/antimine-android){[:MdTgtBlank:]}
* Per giocare da **browser Web**, beh, su questa pagina sto mettendo su qualcosa che per i miei personali gusti è il meglio disponibile. Funziona bene sia su desktop che mobile (anche se lì il tempo per cui bisogna tenere un quadrante premuto per piantare una bandiera è lunghetto), ed ha una grafica minimale.
Sto lavorando per rendere il gioco personalizzabile, per il momento si può solo giocare con ciò che ho implementato come minigioco per sbloccare la lettura della sezione [Curiosità](#-Curiosit) (se non vedi nulla, attiva JavaScript!).
**Nota**: la mia implementazione è basata su [vue-minesweeper](https://github.com/antfu/vue-minesweeper){[:MdTgtBlank:]}, con alcune personali modifiche per l'embedding. Tutto il mio codice modificato è [nella repository del sitoctt]([:sitocttRepoBase:]/-/tree/main/Assets/vuesweeper-core){[:MdTgtBlank:]} (licenza MIT).
</div>
## Altre Risorse
* [Authoritative Minesweeper](https://minesweepergame.com/){[:MdTgtBlank:]}
@ -50,3 +70,5 @@ Personalmente, chiamo il campo minato anche "**minaspazzante**", per scherzare s
[^Regional_Differences]: <https://tcrf.net/Minesweeper_(Windows,_1990)/en#Regional_Differences>{[:MdTgtBlank:]}
[^Windows_Vista_and_Windows_7]: <https://en.wikipedia.org/wiki/Microsoft_Minesweeper#Windows_Vista_and_Windows_7>{[:MdTgtBlank:]}
[^PageBg]: **Sfondo della Pagina**: [Fonte](https://www.techradar.com/news/gaming/the-most-successful-game-ever-a-history-of-minesweeper-596504){[:MdTgtBlank:]}
<script src="/Assets/MinesweeperEmbed.js"></script>

View File

@ -22,7 +22,7 @@
}
</style>
# 🔢 Nonogram
# # Nonogram
I **Nonogram** (conosciuti anche come **Picross**, **Crucipixel**, e un'altra ventina di nomi) sono dei puzzle che consistono nel riempire una griglia di quadratini al fine di svelare un'immagine, utilizzando i numeri ai lati del foglio per trovare intersezioni tra righe e colonne.

View File

@ -25,6 +25,8 @@ _In costruzione..._
Questo è il posto per i titoli migliori, **quelli epici e unici nel loro genere**, punto. E qui, non importa assolutamente che un dato gioco sia popolare o meno: conta solo quanto per me è importante.
### <a href="./2048.html">🔢️ 2048 ↗️</a>
### <span class="twa twa-🎩">🎩</span> A Hat in Time
Platformer 3D vivace e frenetico, proveniente senza dubbio dalla scuola di Super Mario 64. La difficoltà della storia principale è giusta, e il gioco scorre piacevolmente con un alternarsi di temi e ambienti che sa sempre di fresco.
Ma, insomma, già soltanto il carisma del personaggio protagonista fa venire voglia di giocare.
@ -37,7 +39,8 @@ Animal Crossing è uno di quei giochi che va banalmente provato per essere capit
### <span class="twa twa-⛏️">⛏️</span> Minecraft
Credo si possa dire che Minecraft è **il gioco** bene o male moderno che da semplici premesse sviluppa complessità potenzialmente infinita. In senso buono, ovviamente: la complessità va a colpire e a premiare chi la cerca, ma il titolo resta giocabile per chiunque.
All'inizio è fare "case" che sono buchi 4x4 sotto terra, ma dopo diventa creare installazioni titaniche, che siano puramente estetiche o anche funzionali - non dimentichiamo che Minecraft è Turing-complete. E alla fine? Beh, non si arriverà mai alla fine...
### <a href=./Minesweeper.html>💣 Minesweeper ↗️</a>
### <a href="./Minesweeper.html">💣 Minesweeper ↗️</a>
### <span class="twa twa-🏃‍♀️">🏃‍♀️</span> Mirror's Edge
Una serie di giochi in prima persona che, con un ritmo vivace ma non opprimente, mischia platforming parkour e combattimento ravvicinato.
@ -46,6 +49,8 @@ In ogni caso, parliamo di un gioco capace di dare un gran senso di libertà e di
<video frameBorder="0" src="[:YouTube360:]2N1TJP1cxmo" style="Height:Calc(80vw / 16 * 9);"></video>
<cite>Da <a href="[:YouTubeURL:]/2N1TJP1cxmo" [:HTMLTgtBlank:]>YouTube/2N1TJP1cxmo</a>.</cite>
### <a href="./Nonogram.html">#️⃣️ Nonogram ↗️</a>
### <span class="twa twa-🐙">🐙</span> Splatoon
Sparatutto in terza persona che dire diverso dagli altri è poco. Splatoon rompe le aspettative di realismo di guerra del genere, e presenta un gameplay simpatico, fatto di colori accecanti, che non si ferma mai. Non ci sono umani a sparare proiettili con il ferro, ma calamari che spargono inchiostro.
Giocare in Mischie Mollusche (questo il nome delle battaglie online 4-contro-4) è divertente, ma profondità del gioco e inventiva del team di sviluppo si vedono in modalità storia.

View File

@ -4,6 +4,8 @@ Exp.Fs = require('fs');
// https://stackoverflow.com/a/69049676
Exp.importAll = function importAll(){ delete this.importAll; Object.assign(global, this); };
Exp.CssAllHeadings = ' h1, h2, h3, h4, h5, h6';
Exp.TryStringToList = In => {
if (typeof(In) == 'string') {
return In.split(',');

View File

@ -62,3 +62,4 @@ MdTgtBlankNofw = " target="_blank" rel="noopener nofollow
YouTubeURL = https://inv.bp.projectsegfau.lt/
YouTube360 = https://inv.bp.projectsegfau.lt/latest_version?itag=18&id=
videoelemargs-controlmuteloopauto = controls="true" muted="true" loop="true" autoplay="true"
sitocttRepoBase = https://gitlab.com/octtspacc/sitoctt