From c34eca2fd639d3193c26e78a358908e586dfb1af Mon Sep 17 00:00:00 2001 From: vik Date: Mon, 12 Oct 2020 16:04:00 +0100 Subject: [PATCH] 001-12-10-2020 --- .gitignore | 24 ++++ README.md | 15 +++ favicon.ico | Bin 0 -> 894 bytes index.html | 270 ++++++++++++++++++++++++++++++++++++++++++ scripts/controller.js | 99 ++++++++++++++++ scripts/extra/fns.js | 6 + scripts/main.js | 5 + scripts/model.js | 142 ++++++++++++++++++++++ scripts/view.js | 131 ++++++++++++++++++++ styles/main.css | 250 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 942 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 favicon.ico create mode 100644 index.html create mode 100644 scripts/controller.js create mode 100644 scripts/extra/fns.js create mode 100644 scripts/main.js create mode 100644 scripts/model.js create mode 100644 scripts/view.js create mode 100644 styles/main.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2b0d76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.DS_Store +node_modules +/dist +.VSCodeCounter +Thumbs.db +TODO + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..db0f9c9 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Euro Cents + +A web app showing the _Euro Millions_ scam + +See it on action: [Euro Cents](https://vikcch.github.io/euro-cents/) + +## How to use + +* Click on `Generate Numbers` button +* Choose `Bets per week` +* Choose `How many years` +* Click on `Start Simulation` button +* Get a sense on how much money you lose + +Times formula: `Bets-per-week * How-many-years * 52` diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0e5696b95ed9732006f39de5aaa1d46f0181deca GIT binary patch literal 894 zcma)*y9&Zk42B~Pu1?~nqxcRkf{)%kX!sMKHWlaRz+g5B#isY9>LL$S|l08otnGC18*xxdL*0G^sLa|ga@yQMzb%qUlJ~20R;#(^QPda7>NQFuh zCzu*rpP27${$OGY`z!Nx%`Lw?Ft2Xi^3?_Nmj`B5?!P?!BaA7yyRbx9VPuiWBZwj5doWa{!O=#F;~#GpCFI7$A#-4|TL@t818C5HllK^IT9(4k JI + + + + + + Euro Cents + + + + + + + + +
+

Euro Cents

+

How much money do you lose?

+
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+ + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Numbers MatchedOddsAvg. Prize Per WinnerWins
+
Match 5 and 2 Stars
+
+ ◯◯◯◯◯ + ★★ +
+
139,838,159 : 1€ 51,991,282.530
+
Match 5 and 1 Star
+
+ ◯◯◯◯◯ + +
+
6,991,907 : 1€ 444,670.050
+
Match 5
+
+ ◯◯◯◯◯ +
+
3,107,514 : 1€ 72,147.880
+
Match 4 and 2 Stars
+
+ ◯◯◯◯ + ★★ +
+
621,502 : 1€ 4,056.510
+
Match 4 and 1 Star
+
+ ◯◯◯◯ + +
+
31,075 : 1€ 191.23 0
+
Match 3 and 2 Stars
+
+ ◯◯◯ + ★★ +
+
14,125 : 1€ 79.850
+
Match 4
+
+ ◯◯◯◯ +
+
13,820 : 1€ 84.240
+
Match 2 and 2 Stars
+
+ ◯◯ + ★★ +
+
985 : 1€ 19.550
+
Match 3 and 1 Star
+
+ ◯◯◯ + +
+
706 : 1€ 14.410
+
Match 3
+
+ ◯◯◯ +
+
313 : 1€ 12.000
+
Match 1 and 2 Stars
+
+ + ★★ +
+
187 : 1€ 10.390
+
Match 2 and 1 Star
+
+ ◯◯ + +
+
49 : 1€ 7.880
+
Match 2
+
+ ◯◯ +
+
21 : 1€ 4.160
+ +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +
+ +
+
Times: 0 / 5,200
+
Money Invested: € 0.00
+
Money Won: € 0.00
+
Profit: € 0.00
+
+ +
+ + +
+ +
+ +
+
+
+
+
+ +
+
+ +
+ +
+ + + + + + \ No newline at end of file diff --git a/scripts/controller.js b/scripts/controller.js new file mode 100644 index 0000000..d454281 --- /dev/null +++ b/scripts/controller.js @@ -0,0 +1,99 @@ +import fns from './extra/fns.js'; +import Model from './model.js'; +import View from './view.js'; + +export default class Controller { + + /** + * + * @param {View} view + * @param {Model} model + */ + constructor(model, view) { + + this.model = model; + this.view = view; + + const handlers = { + + masterKey: this.handlerMasterKey_Click, + keys: this.handlerKeys_Click, + betsPerWeek: this.handlerBetsPerWeek_Input, + years: this.handlerYears_Input + }; + + this.view.bindControls(handlers); + + this.view.updateStats(0, this.model); + this.view.keysButton.disabled = true; + } + + /** + * + * @param {Event} event + */ + handlerMasterKey_Click = (event) => { + + event.currentTarget.disabled = true; + + this.model.generateMasterKey(); + + this.view.fillMasters(this.model); + } + + /** + * + * @param {Event} event + */ + handlerKeys_Click = (event) => { + + const times = this.view.totalTimes; + + let count = 1; + + const interval = setInterval(() => { + + this.model.generateSlaveKey(); + + const prizeLabel = this.model.matchMaster(); + + this.view.fillSlaves(this.model); + + this.view.updateStats(count, this.model); + + if (count === times || prizeLabel === '52') { + + clearInterval(interval); + + if (prizeLabel === '52') { + + alert('OMG!'); + } + } + + count++; + + }, 0); + + event.currentTarget.disabled = true; + } + + handlerBetsPerWeek_Input = (event) => { + + const { value } = event.currentTarget; + + event.currentTarget.value = fns.onlyNumbers(value); + + this.view.updateStats(0, this.model); + } + + handlerYears_Input = (event) => { + + const { value } = event.currentTarget; + + event.currentTarget.value = fns.onlyNumbers(value); + + this.view.updateStats(0, this.model); + } + +} \ No newline at end of file diff --git a/scripts/extra/fns.js b/scripts/extra/fns.js new file mode 100644 index 0000000..80b4c23 --- /dev/null +++ b/scripts/extra/fns.js @@ -0,0 +1,6 @@ + +export default { + + head: array => array[0], + onlyNumbers: value => value.replace(/[^\d]/, '') +}; \ No newline at end of file diff --git a/scripts/main.js b/scripts/main.js new file mode 100644 index 0000000..1334683 --- /dev/null +++ b/scripts/main.js @@ -0,0 +1,5 @@ +import Controller from './controller.js'; +import Model from './model.js'; +import View from './view.js'; + +new Controller(new Model, new View); \ No newline at end of file diff --git a/scripts/model.js b/scripts/model.js new file mode 100644 index 0000000..4c66a2c --- /dev/null +++ b/scripts/model.js @@ -0,0 +1,142 @@ +import fns from './extra/fns.js'; + +export default class Model { + + constructor() { + + this.masterNumbers = Array(5); + this.masterStars = Array(2); + + this.slaveNumbers = Array(5); + this.slaveStars = Array(2); + + this.allNumbers = Array.from(Array(50), (v, k) => k + 1); + this.allStars = Array.from(Array(12), (v, k) => k + 1); + + + + + + + + + // charAt(0) => Number + // charAt(1) => Star + this.prizes = { + // '52': 0, + // '30': 0, + // '12': 0, + // '21': 0, + // '20': 0, + // // Sem premio + // '02': 0, + // '01': 0, + // '10': 0, + // '00': 0 + }; + + const prizesLabel = ['52', '51', '50', '42', '41', '32', '40', '22', '31', '30', '12', '21', '20']; + + const valuesOnTable = document.querySelectorAll('.prize'); + + prizesLabel.forEach((label, i) => { + this.prizes[label] = { + wins: 0, + amount: Number(valuesOnTable[i].innerText.replace(/,/g, '').slice(2)) + }; + }); + + + this.moneyWon = 0; + } + + generateKey() { + + const rnd = arr => () => fns.head(arr.splice(Math.random() * arr.length, 1)); + + return { + numbers: Array.from(Array(5), rnd(this.allNumbers.slice())), + stars: Array.from(Array(2), rnd(this.allStars.slice())) + }; + + } + + /* generateKey() { + + + const numbers = new Set([(Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1]); + + while (numbers.size !== 5) numbers.add((Math.random() * 50) + 1); + + const stars = new Set([(Math.random() * 12) + 1, (Math.random() * 12) + 1]); + while (stars.size !== 2) stars.add((Math.random() * 12) + 1); + + return { + numbers: [...numbers], + stars: [...stars] + }; + + } */ + + // Solução mais muito mais rapida, mas não faz diferença porque o + // bottle neck e o setInterval + // tinha que ficar apenas com a parte inteira + + /* generateKey() { + + const numbers = [(Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1, (Math.random() * 50) + 1]; + + while (numbers.length !== 5) { + + const n = Math.random() * 50 + 1; + if (!numbers.includes(n)) numbers.push(n); + } + + const stars = [(Math.random() * 12) + 1, (Math.random() * 12) + 1]; + while (stars.length !== 2) { + + const n = Math.random() * 12 + 1; + if (!stars.includes(n)) stars.push(n); + } + + return { numbers, stars }; + } */ + + generateMasterKey() { + + const r = this.generateKey(); + + this.masterNumbers = r.numbers; + this.masterStars = r.stars; + } + + generateSlaveKey() { + + const r = this.generateKey(); + + this.slaveNumbers = r.numbers; + this.slaveStars = r.stars; + } + + matchMaster() { + + const setNumbers = new Set([...this.masterNumbers, ...this.slaveNumbers]); + + const numbersCount = 10 - setNumbers.size; + + const setStars = new Set([...this.masterStars, ...this.slaveStars]); + + const starsCount = 4 - setStars.size; + + const prizeLabel = `${numbersCount}${starsCount}`; + + if (this.prizes[prizeLabel]) { + + this.prizes[prizeLabel].wins++; + this.moneyWon += this.prizes[prizeLabel].amount; + } + + return prizeLabel; + } + +} \ No newline at end of file diff --git a/scripts/view.js b/scripts/view.js new file mode 100644 index 0000000..74d3db8 --- /dev/null +++ b/scripts/view.js @@ -0,0 +1,131 @@ +import Model from './model.js'; + +export default class View { + + constructor() { + + this.masterNumbers = document.querySelectorAll('.number-master'); + this.masterStars = document.querySelectorAll('.star-master'); + this.masterKeyButton = document.querySelector('#master-key-btn'); + + this.slaveNumbers = document.querySelectorAll('.number-slave'); + this.slaveStars = document.querySelectorAll('.star-slave'); + this.keysButton = document.querySelector('#keys-btn'); + + + this.years = document.querySelector('#years'); + this.betsPerWeek = document.querySelector('#bets-per-week'); + + this.moneySpend = document.querySelector('#money-spend'); + this.moneyWon = document.querySelector('#money-won'); + this.profit = document.querySelector('#profit'); + + this.keysCounter = document.querySelector('#keys-counter'); + + this.table = document.querySelectorAll('.wins'); + } + + get totalTimes() { + + const years = Number(this.years.value); + const betsPerWeek = Number(this.betsPerWeek.value); + + return years * 52 * betsPerWeek; + } + + bindControls(handlers) { + + this.masterKeyButton.addEventListener('click', handlers.masterKey); + this.keysButton.addEventListener('click', handlers.keys); + + this.betsPerWeek.addEventListener('input', handlers.betsPerWeek); + this.years.addEventListener('input', handlers.years); + } + + /** + * + * @param {Model} model + */ + fillMasters(model) { + + const fps = 60; + + const frameRate = 1000 / fps; + + const { masterNumbers, masterStars } = model; + + let current = 0; + + let index = -1; + + setInterval(() => { + + const { numbers, stars } = model.generateKey(); + + masterNumbers.forEach((n, i) => { + + const temp = i < index ? n : numbers[i]; + this.masterNumbers[i].innerHTML = temp; + }); + + masterStars.forEach((n, i) => { + + const temp = i + 5 < index ? n : stars[i]; + this.masterStars[i].innerHTML = temp; + }); + + if (current % 15 === 0) index++; + + if (index == 7) this.keysButton.disabled = false; + + current++; + + }, frameRate); + } + + fillSlaves({ slaveNumbers, slaveStars }) { + + slaveNumbers.forEach((n, i) => this.slaveNumbers[i].innerHTML = n); + slaveStars.forEach((n, i) => this.slaveStars[i].innerHTML = n); + } + + + updateStats(count, model) { + + const prizesLabel = ['52', '51', '50', '42', '41', '32', '40', '22', '31', '30', '12', '21', '20']; + + const { prizes, moneyWon } = model; + + // this.table.forEach((el, i) => el.innerHTML = prizes[prizesLabel[i]] || 0); + + this.table.forEach((el, i) => { + + const label = prizesLabel[i]; + el.innerHTML = prizes[label].wins; + }); + + const moneySpend = count * 2.3; + const profit = moneyWon - moneySpend; + + const totalTimes = this.totalTimes === 0 + ? `Infinity or 1st Prize` + : this.totalTimes.toLocaleString('en-US'); + + this.keysCounter.innerHTML = `Times: ${count.toLocaleString('en-US')} / ${totalTimes}`; + this.moneySpend.innerHTML = `Money Invested: € ${moneySpend.toLocaleString('en-US', { minimumFractionDigits: 2 })}`; + this.moneyWon.innerHTML = `Money Won: € ${moneyWon.toLocaleString('en-US', { minimumFractionDigits: 2 })}`; + this.profit.innerHTML = `€ ${profit.toLocaleString('en-US', { minimumFractionDigits: 2 })}`; + + this.updateProfitCSS(profit); + } + + updateProfitCSS(profit) { + + const color = profit > 0 ? "green" : "red"; + + this.profit.style.color = color; + } + +} + +// ("51,991,282.53").replace(/,/g,'') \ No newline at end of file diff --git a/styles/main.css b/styles/main.css new file mode 100644 index 0000000..f6eef5c --- /dev/null +++ b/styles/main.css @@ -0,0 +1,250 @@ +@import url("https://fonts.googleapis.com/css?family=Open+Sans&display=swap"); +* { + padding: 0; + margin: 0; + box-sizing: border-box; +} + +html { + font-family: "Open Sans", Arial, Helvetica, sans-serif; + font-size: 14px; + color: #6a737c; +} + +body { + max-width: 960px; + margin: auto; +} + +body>*+* { + margin-top: 16px; +} + +.train { + display: flex; + flex-direction: row; +} + +.master-key { + margin-bottom: 16px; +} + +.master-key>*+* { + margin-left: 6px; +} + +.number-master { + width: 40px; + height: 40px; + font-size: 20px; + border-radius: 50%; + border: 2px solid lightgray; +} + +.star-master { + width: 40px; + height: 40px; + font-size: 20px; + background: url('data:image/png;base64,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'); + background-repeat: no-repeat; + background-size: 40px 40px; +} + +.slave-key { + margin-bottom: 12px; +} + +.slave-key>*+* { + margin-left: 12px; +} + +.number-slave { + width: 30px; + height: 30px; + font-size: 12px; + border-radius: 50%; + border: 2px solid lightgray; +} + +.star-slave { + width: 30px; + height: 30px; + font-size: 12px; + background: url('data:image/png;base64,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'); + background-repeat: no-repeat; + background-size: 30px 30px; +} + +.star-icon { + color: chocolate; +} + +table { + border-collapse: collapse; + font-size: 10.75px; + width: 100%; + background-color: #fafafa; + border-radius: 3px; + border: 1px solid #b4b4b4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset; +} + +table, th, td { + border: 1px solid gray; +} + +td { + text-align: right; +} + +thead { + background-color: #f0f0f0; +} + +input { + padding: 4px; +} + +button { + padding: 12px 32px; + border-radius: 3px; + cursor: pointer; + font-size: 18px; + text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); + box-shadow: inset 0 1px 0 hsla(0, 0%, 100%, .2); + min-width: 96px; +} + +.button--green { + color: #fff; + background-color: #218838; + border: 1px solid #1e7e34; + text-shadow: 1px 1px 0px #777777; +} + +.button--blue { + color: white; + background-color: #326ebe; + border: 1px solid #2e6fb9; + text-shadow: 1px 1px 0px #777777; +} + +.button:enabled:hover { + background-image: linear-gradient(rgba(0, 0, 0, .1), rgba(0, 0, 0, .1)); +} + +.button:disabled { + color: #777777; + cursor: default; + border: solid 1px #dcdcdc; + background: linear-gradient(180deg, #ededed 5%, #dfdfdf 100%); + text-shadow: 1px 1px 0px #ffffff; + box-shadow: inset 1px 1px 0px 0px #ffffff; +} + +.badge { + padding: 8px; + background-color: #fafafa; + border-radius: 3px; + border: 1px solid #b4b4b4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset; +} + +.center { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.text-center { + text-align: center; +} + +a { + color: #007bff; +} + +.square-icon { + font-size: 10.75px; +} + +.star-icon { + font-size: 13px; +} + +.very-small-text { + font-size: 11px; +} + +footer>*+* { + margin-top: 4px; +} + +.tm-l-responsive { + margin-top: 16px; +} + +.tm-l { + margin-top: 16px; +} + +.tm-s { + margin-top: 4px; +} + +kbd { + background-color: #eee; + border-radius: 3px; + border: 1px solid #b4b4b4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset; + color: #333; + display: inline-block; + /* font-family: "Lucida Console", Monaco, monospace; */ + font-size: 14px; + font-weight: 700; + line-height: 1; + padding: 2px 4px; + white-space: nowrap; +} + +@media only screen and (min-width: 769px) { + .number-master { + width: 60px; + height: 60px; + font-size: 24px; + } + .star-master { + width: 60px; + height: 60px; + font-size: 24px; + background-size: 60px 60px; + } + .number-slave { + width: 45px; + height: 45px; + font-size: 18px; + border-radius: 50%; + border: 2px solid lightgray; + } + .star-slave { + width: 45px; + height: 45px; + font-size: 18px; + background-size: 45px 45px; + } + table { + width: auto; + } + table, th, td { + padding: 8px; + } + .divorced-pc { + display: flex; + flex-direction: row; + justify-content: space-between; + } + .tm-l-responsive { + margin-top: 0px; + } +} \ No newline at end of file