From 10599ab2e789ffb93a19f6aa3c100f533c460315 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sun, 29 Oct 2023 11:32:06 -0400 Subject: generate, show, solve puzzles --- app/assets/config/wittle_manifest.js | 1 + app/assets/images/wittle/slider.png | Bin 0 -> 20100 bytes app/assets/javascripts/wittle/application.js | 1 + app/assets/javascripts/wittle/utilities.js.erb | 204 ----------------------- app/assets/javascripts/wittle/wittle.js | 5 + app/assets/stylesheets/wittle/general.css.scss | 91 ++++++++++ app/controllers/wittle/puzzles_controller.rb | 36 +++- app/models/wittle/puzzle.rb | 4 + app/views/layouts/wittle/application.html.haml | 3 +- app/views/wittle/puzzles/_handle_puzzle.html.erb | 33 ++++ app/views/wittle/puzzles/_submission.html.haml | 7 + app/views/wittle/puzzles/about.html.haml | 3 + app/views/wittle/puzzles/index.html.haml | 13 -- app/views/wittle/puzzles/show.html.haml | 25 +++ app/views/wittle/puzzles/solve.js.erb | 1 + 15 files changed, 208 insertions(+), 219 deletions(-) create mode 100644 app/assets/images/wittle/slider.png create mode 100644 app/assets/javascripts/wittle/wittle.js create mode 100644 app/assets/stylesheets/wittle/general.css.scss create mode 100644 app/views/wittle/puzzles/_handle_puzzle.html.erb create mode 100644 app/views/wittle/puzzles/_submission.html.haml create mode 100644 app/views/wittle/puzzles/about.html.haml create mode 100644 app/views/wittle/puzzles/show.html.haml create mode 100644 app/views/wittle/puzzles/solve.js.erb (limited to 'app') diff --git a/app/assets/config/wittle_manifest.js b/app/assets/config/wittle_manifest.js index 079ddad..46aea2a 100644 --- a/app/assets/config/wittle_manifest.js +++ b/app/assets/config/wittle_manifest.js @@ -1,3 +1,4 @@ //= link_directory ../stylesheets/wittle .css //= link_directory ../javascripts/wittle .js //= link_directory ../audio/wittle .aac +//= link_directory ../images/wittle .png diff --git a/app/assets/images/wittle/slider.png b/app/assets/images/wittle/slider.png new file mode 100644 index 0000000..f093f89 Binary files /dev/null and b/app/assets/images/wittle/slider.png differ diff --git a/app/assets/javascripts/wittle/application.js b/app/assets/javascripts/wittle/application.js index e54c646..52d2214 100644 --- a/app/assets/javascripts/wittle/application.js +++ b/app/assets/javascripts/wittle/application.js @@ -10,4 +10,5 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +//= require jquery3 //= require_tree . diff --git a/app/assets/javascripts/wittle/utilities.js.erb b/app/assets/javascripts/wittle/utilities.js.erb index 406adda..487af6e 100644 --- a/app/assets/javascripts/wittle/utilities.js.erb +++ b/app/assets/javascripts/wittle/utilities.js.erb @@ -245,210 +245,6 @@ window.deleteElementsByClassName = function(rootElem, className) { } } -window.loadHeader = function(titleText) { - document.body.style.marginLeft = '0px' - - var navbar = document.createElement('div') - document.body.appendChild(navbar) - navbar.className = 'navbar' - navbar.style = 'min-width: 700px; position: absolute; top: 0; width: 100%; z-index: 1' - navbar.style.borderBottom = '2px solid ' + window.BORDER - navbar.style.background = window.PAGE_BACKGROUND - - var navbarPadding = document.createElement('div') - document.body.appendChild(navbarPadding) - navbarPadding.className = 'navbar-padding' - - var titleDiv = document.createElement('div') - navbar.appendChild(titleDiv) - titleDiv.style = 'position: absolute; width: 100%; pointer-events: none' - - var titleLabel = document.createElement('label') - titleDiv.appendChild(titleLabel) - titleLabel.style = 'font-size: 48; pointer-events: auto' - titleLabel.id = 'title' - titleLabel.innerText = titleText - - var link = document.createElement('label') - navbar.appendChild(link) - link.style = 'float: left; margin-left: 32px; cursor: pointer; line-height: 60px' - link.className = 'navbar-content' - - if (window.location.href.endsWith('browse.html')) { - navbar.style.position = 'fixed' // When browsing, pin the navbar to the top so that it's visible during infinite scroll. - - link.innerText = 'Create a puzzle' - link.onpointerdown = function() {window.location = 'editor.html'} - - var link2 = document.createElement('label') - navbar.appendChild(link2) - link2.style = 'float: left; margin-left: 20px; cursor: pointer; line-height: 60px; display: none' - link2.className = 'navbar-content' - link2.innerText = 'Jump to top' - link2.id = 'scrollToTop' - link2.onpointerdown = function() {window.scrollTo(0, 0)} - - } else if (window.location.href.includes('/play/')) { - link.innerText = 'Back to all puzzles' - link.onpointerdown = function() {window.location = '../browse.html'} - } else /* All other pages */ { - link.innerText = 'Browse all puzzles' - link.onpointerdown = function() {window.location = 'browse.html'} - } - - var feedbackButton = document.createElement('label') - navbar.appendChild(feedbackButton) - feedbackButton.id = 'feedbackButton' - feedbackButton.style = 'float: right; margin-right: 8px; cursor: pointer; line-height: 60px' - feedbackButton.innerText = 'Send feedback' - feedbackButton.className = 'navbar-content' - feedbackButton.onpointerdown = function() { - var feedback = prompt('Provide feedback:') - if (feedback) { - sendFeedback(feedback) - } - } - - var separator = document.createElement('label') - navbar.appendChild(separator) - separator.style = 'float: right; line-height: 60px; padding-left: 6px; padding-right: 6px' - separator.className = 'navbar-content' - separator.innerText = '|' - - var sourceLink = document.createElement('label') - navbar.appendChild(sourceLink) - sourceLink.style = 'float: right; line-height: 60px; cursor: pointer' - sourceLink.innerText = 'Source code' - sourceLink.className = 'navbar-content' - sourceLink.onpointerdown = function() {window.open('https://github.com/jbzdarkid/jbzdarkid.github.io', '_blank')} - - var collapsedSettings = drawSymbol({'type': 'plus', 'width':20, 'height':20}) - navbar.appendChild(collapsedSettings) - collapsedSettings.style = 'width: 20px; height: 20px; position: absolute; left: 0; cursor: pointer' - collapsedSettings.style.border = '2px solid ' + window.BORDER - collapsedSettings.id = 'collapsedSettings' - collapsedSettings.onpointerdown = function() { - this.style.display = 'none' - var expandedSettings = document.getElementById('expandedSettings') - expandedSettings.style.display = null - window.settings.expanded = 'true' - } - - var expandedSettings = document.createElement('div') - navbar.appendChild(expandedSettings) - expandedSettings.style = 'width: 300px; position: absolute; left: 0; display: none; padding: 10px' - expandedSettings.style.border = '2px solid ' + window.BORDER - expandedSettings.style.background = window.PAGE_BACKGROUND - expandedSettings.id = 'expandedSettings' - - var minus = drawSymbol({'type':'minus', 'width':20, 'height':20}) - minus.style = 'width: 20px; height: 20px; cursor: pointer; position: absolute; top: 0; left: 0' - expandedSettings.appendChild(minus) - minus.onpointerdown = function() { - this.parentElement.style.display = 'none' - var collapsedSettings = document.getElementById('collapsedSettings') - collapsedSettings.style.display = null - window.settings.expanded = 'false' - } - - if (window.settings.expanded == 'true') { - collapsedSettings.onpointerdown() - } - - // Now, for the contents of the settings - var settingsLabel = document.createElement('label') - expandedSettings.appendChild(settingsLabel) - settingsLabel.innerText = 'settings' - settingsLabel.style = 'line-height: 0px' // Attach to the top - - expandedSettings.appendChild(document.createElement('br')) - expandedSettings.appendChild(document.createElement('br')) - - // Theme - document.body.style.color = window.TEXT_COLOR - document.body.style.background = window.PAGE_BACKGROUND - var themeButton = document.createElement('button') - expandedSettings.appendChild(themeButton) - if (window.settings.theme == 'night') { - themeButton.innerText = 'Night theme' - themeButton.onpointerdown = function() { - window.settings.theme = 'light' - location.reload() - } - } else if (window.settings.theme == 'light') { - themeButton.innerText = 'Light theme' - themeButton.onpointerdown = function() { - window.settings.theme = 'night' - location.reload() - } - } - - expandedSettings.appendChild(document.createElement('br')) - - // Sensitivity - var sensLabel = document.createElement('label') - expandedSettings.appendChild(sensLabel) - sensLabel.htmlFor = 'sens' - sensLabel.innerText = 'Mouse Speed 2D' - - var sens = document.createElement('input') - expandedSettings.appendChild(sens) - sens.type = 'range' - sens.id = 'sens' - sens.min = '0.1' - sens.max = '1.3' - sens.step = '0.1' - sens.value = window.settings.sensitivity - sens.onchange = function() { - window.settings.sensitivity = this.value - } - sens.style.backgroundImage = 'linear-gradient(to right, ' + window.ALT_BACKGROUND + ', ' + window.ACTIVE_COLOR + ')' - - // Volume - var volumeLabel = document.createElement('label') - expandedSettings.appendChild(volumeLabel) - volumeLabel.htmlFor = 'volume' - volumeLabel.innerText = 'Volume' - - var volume = document.createElement('input') - expandedSettings.appendChild(volume) - volume.type = 'range' - volume.id = 'volume' - volume.min = '0' - volume.max = '0.24' - volume.step = '0.02' - volume.value = parseFloat(window.settings.volume) - volume.onchange = function() { - window.settings.volume = this.value - } - volume.style.backgroundImage = 'linear-gradient(to right, ' + window.ALT_BACKGROUND + ', ' + window.ACTIVE_COLOR + ')' - - // Custom mechanics -- disabled for now - window.settings.customMechanics = false - /* - var customMechanics = createCheckbox() - expandedSettings.appendChild(customMechanics) - customMechanics.id = 'customMechanics' - if (window.settings.customMechanics == 'true') { - customMechanics.style.background = window.BORDER - customMechanics.checked = true - } - - customMechanics.onpointerdown = function() { - this.checked = !this.checked - this.style.background = (this.checked ? window.BORDER : window.PAGE_BACKGROUND) - window.settings.customMechanics = this.checked - window.location.reload() - } - - var mechLabel = document.createElement('label') - expandedSettings.appendChild(mechLabel) - mechLabel.style.marginLeft = '6px' - mechLabel.htmlFor = 'customMechanics' - mechLabel.innerText = 'Custom mechanics' - */ -} - // Automatically solve the puzzle window.solvePuzzle = function() { if (window.setSolveMode) window.setSolveMode(false) diff --git a/app/assets/javascripts/wittle/wittle.js b/app/assets/javascripts/wittle/wittle.js new file mode 100644 index 0000000..883a4b8 --- /dev/null +++ b/app/assets/javascripts/wittle/wittle.js @@ -0,0 +1,5 @@ +$.ajaxSetup({ + headers: { + 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') + } +}); diff --git a/app/assets/stylesheets/wittle/general.css.scss b/app/assets/stylesheets/wittle/general.css.scss new file mode 100644 index 0000000..06a7a83 --- /dev/null +++ b/app/assets/stylesheets/wittle/general.css.scss @@ -0,0 +1,91 @@ +#wrap { + +} + +h1 { + text-align: center; +} + +#trace-settings { + margin: 1em auto; + width: 540px; + + summary { + text-align: center; + } +} + +#sens, #volume { + background-image: linear-gradient(to right, #EEE, #555); +} + +/* Slider control main bar */ +input[type="range"] { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + border-radius: 3px; + height: 15px; + margin-bottom: 5px; + margin-top: 5px; + outline: none; + width: 100%; +} +/* Slider control icon */ +input[type="range"]::-webkit-slider-thumb { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: image-url('wittle/slider.png'); + background-size: 17px 33px; + height: 33px; + width: 17px; +} +input[type="range"]::-moz-range-thumb { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: image-url('wittle/slider.png'); + background-size: 17px 33px; + height: 33px; + width: 17px; +} +input[type="range"]::-ms-thumb { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: image-url('wittle/slider.png'); + background-size: 17px 33px; + height: 33px; + width: 17px; +} + +#submission-form { + margin: 1em auto; + width: 540px; + text-align: center; + + form { + border: 1px solid black; + width: max-content; + padding: 0 2em; + margin: 0 auto; + } +} + +#scores { + display: flex; + + div { + flex: 0 0 48%; + + h2 { + text-align: center; + } + + ol { + width: max-content; + margin: 0 auto; + } + } +} diff --git a/app/controllers/wittle/puzzles_controller.rb b/app/controllers/wittle/puzzles_controller.rb index 09a2524..9599307 100644 --- a/app/controllers/wittle/puzzles_controller.rb +++ b/app/controllers/wittle/puzzles_controller.rb @@ -1,7 +1,41 @@ module Wittle class PuzzlesController < ApplicationController + def about + @normal_puzzle = Puzzle.normal.order(created_at: :desc).first + @hard_puzzle = Puzzle.hard.order(created_at: :desc).first + end + def index - @puzzle = WittleGenerator.new.generate_medium + #@puzzle = WittleGenerator.new.generate_medium + end + + def show + @puzzle = Puzzle.find(params[:id]) + @playable = @puzzle.latest? && !((session[:played_puzzles] || []).include? @puzzle.id) + end + + def solve + @puzzle = Puzzle.find(params[:id]) + + raise ActiveRecord::RecordNotFound unless @puzzle.latest? + + if @puzzle.solved_data.nil? + @puzzle.solved_data = params[:solved] + @puzzle.save! + end + + session[:played_puzzles] ||= [] + session[:played_puzzles] << @puzzle.id + end + + def submit + @puzzle = Puzzle.find(params[:id]) + + raise ActiveRecord::RecordNotFound unless @puzzle.latest? + + @puzzle.scores.create!(name: params[:name], ip: request.ip) + + redirect_to @puzzle end end end diff --git a/app/models/wittle/puzzle.rb b/app/models/wittle/puzzle.rb index e118d8a..f9009bc 100644 --- a/app/models/wittle/puzzle.rb +++ b/app/models/wittle/puzzle.rb @@ -8,5 +8,9 @@ module Wittle validates :category, presence: true enumerize :category, in: [:normal, :hard, :expert], scope: :shallow + + def latest? + Puzzle.where(category: category).order(created_at: :desc).first.id == id + end end end diff --git a/app/views/layouts/wittle/application.html.haml b/app/views/layouts/wittle/application.html.haml index 945ffc0..80519f4 100644 --- a/app/views/layouts/wittle/application.html.haml +++ b/app/views/layouts/wittle/application.html.haml @@ -6,4 +6,5 @@ = csp_meta_tag = stylesheet_link_tag "wittle/application", media: "all" = javascript_include_tag "wittle/application" - %body= yield + %body + #wrap= yield diff --git a/app/views/wittle/puzzles/_handle_puzzle.html.erb b/app/views/wittle/puzzles/_handle_puzzle.html.erb new file mode 100644 index 0000000..3ac868e --- /dev/null +++ b/app/views/wittle/puzzles/_handle_puzzle.html.erb @@ -0,0 +1,33 @@ + diff --git a/app/views/wittle/puzzles/_submission.html.haml b/app/views/wittle/puzzles/_submission.html.haml new file mode 100644 index 0000000..744372a --- /dev/null +++ b/app/views/wittle/puzzles/_submission.html.haml @@ -0,0 +1,7 @@ +%h3 Congrats! +%p Would you like to submit your time? += form_with url: submit_puzzle_path(@puzzle) do |form| + %p + = form.label :name, "Name:" + = form.text_field :name + %p= form.submit "Submit" diff --git a/app/views/wittle/puzzles/about.html.haml b/app/views/wittle/puzzles/about.html.haml new file mode 100644 index 0000000..628ba0b --- /dev/null +++ b/app/views/wittle/puzzles/about.html.haml @@ -0,0 +1,3 @@ +%p by Hatkirby, with help from Sigma144 and jbzdarkid +%p= link_to "Normal", @normal_puzzle +%p= link_to "Hard", @hard_puzzle diff --git a/app/views/wittle/puzzles/index.html.haml b/app/views/wittle/puzzles/index.html.haml index 9e12e8b..e69de29 100644 --- a/app/views/wittle/puzzles/index.html.haml +++ b/app/views/wittle/puzzles/index.html.haml @@ -1,13 +0,0 @@ -%h1 Puzzles#index -%p Find me in app/views/wittle/puzzles/index.html.haml -:javascript - loadHeader("Unnamed Puzzle") -%div{ style: "display: flex; justify-content: center; align-items: center"} - %svg#puzzle{ style: "pointer-events: auto"} -:javascript - window.onload = function() { - //var puzzle = window.deserializePuzzle("_AAAAAA8PCQAAAEdlbmVyYXRlZAABAAAACAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAgAAQAAAAACAAAA/wABAAAAAAgAAQAAAAAIAAEAAAAACAABAAAAAAgAAQAAAAAIAAEAAQAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQABAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAAIAAEAAAAACAABAAAAAAgAAQAAAAAIAAEAAAAAAv////8AAQAAAAAIAAEAAAAAAv////8AAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAQEAAAAAAQAAAAABAAAAAAEAAQAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAIAAAD/AAEAAAAACAABAAAAAAgAAQAAAAAIAAEAAAAACAABAAAAAAgAAQAAAAAIAAEAAAAAAQAAAAEBAAAAAAEAAAAAAQAAAAABAAAAAQEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAAIAAEAAQAAAv////8AAQAAAAAIAAEAAAAAAv////8AAQAAAAACAAAA/wABAAAAAAIAAAD/AAEAAAAAAgAAAP8AAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAL/////AAEAAAAAAv////8AAQAAAAAIAAEAAAAACAABAAAAAAgAAQAAAAACAAAA/wABAAAAAAgAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAQAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAABAQAAAAABAAEAAAEAAAAAAQAAAAABAAAAAAL/////AAEAAAAAAgAAAP8AAQAAAAAIAAEAAAAAAv////8AAQAAAAACAAAA/wABAAAAAAIAAAD/AAEAAAAACAABAAEAAAEAAAAAAQABAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAgAAAP8AAQAAAAAIAAEAAAAAAv////8AAQAAAAAIAAEAAAAAAv////8AAQAAAAAIAAEAAAAACAABAAAAAAEAAAAIAQAAAAABAAAAAAEAAAAAAQABAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAAAAAQAAAAABAAAAAAEAAQAAAQAAAAABAAAAEAAAAAAN") - var puzzle = window.deserializePuzzle("#{@puzzle}") - draw(puzzle) - } - diff --git a/app/views/wittle/puzzles/show.html.haml b/app/views/wittle/puzzles/show.html.haml new file mode 100644 index 0000000..d5d8b00 --- /dev/null +++ b/app/views/wittle/puzzles/show.html.haml @@ -0,0 +1,25 @@ +%h1 Wittle ##{@puzzle.id} +#puzzle-container{ style: "display: flex; justify-content: center; align-items: center" } + %svg#puzzle{ style: "pointer-events: auto"} +#submission-form +- if @playable + %details#trace-settings + %summary Settings + .things + %label{ for: "sens" } Mouse Speed 2D + %input#sens{ type: "range", min: "0.1", max: "1.3", step: "0.1" } + %label{ for: "volume" } Volume + %input#volume{ type: "range", min: "0", max: "0.24", step: "0.02" } +- else + #scores + #by-time + %h2 Fastest Solves + %ol + - @puzzle.scores.where("seconds_taken IS NOT NULL").order(seconds_taken: :asc).each do |score| + %li= score.name + #by-when + %h2 Completion Order + %ol + - @puzzle.scores.order(created_at: :desc).each do |score| + %li= score.name += render partial: "handle_puzzle" diff --git a/app/views/wittle/puzzles/solve.js.erb b/app/views/wittle/puzzles/solve.js.erb new file mode 100644 index 0000000..12c2350 --- /dev/null +++ b/app/views/wittle/puzzles/solve.js.erb @@ -0,0 +1 @@ +$("#submission-form").html('<%= escape_javascript(render partial: "submission") %>'); -- cgit 1.4.1