commit
					f234077cd7
				
				 12 changed files with 695 additions and 0 deletions
			
			
		@ -0,0 +1,14 @@
				@@ -0,0 +1,14 @@
					 | 
				
			||||
.git | 
				
			||||
/resources/public/js/compiled/** | 
				
			||||
figwheel_server.log | 
				
			||||
pom.xml | 
				
			||||
*jar | 
				
			||||
/lib/ | 
				
			||||
/classes/ | 
				
			||||
/out/ | 
				
			||||
/target/ | 
				
			||||
.lein-deps-sum | 
				
			||||
.lein-repl-history | 
				
			||||
.lein-plugins/ | 
				
			||||
.repl | 
				
			||||
.nrepl-port | 
				
			||||
@ -0,0 +1,13 @@
				@@ -0,0 +1,13 @@
					 | 
				
			||||
/resources/public/js/compiled/** | 
				
			||||
figwheel_server.log | 
				
			||||
pom.xml | 
				
			||||
*jar | 
				
			||||
/lib/ | 
				
			||||
/classes/ | 
				
			||||
/out/ | 
				
			||||
/target/ | 
				
			||||
.lein-deps-sum | 
				
			||||
.lein-repl-history | 
				
			||||
.lein-plugins/ | 
				
			||||
.repl | 
				
			||||
.nrepl-port | 
				
			||||
@ -0,0 +1,10 @@
				@@ -0,0 +1,10 @@
					 | 
				
			||||
#stage 1 | 
				
			||||
FROM debian as debian | 
				
			||||
WORKDIR /app | 
				
			||||
COPY . . | 
				
			||||
RUN apt update && apt install -y leiningen | 
				
			||||
RUN lein cljsbuild once | 
				
			||||
 | 
				
			||||
#stage 2 | 
				
			||||
FROM nginx | 
				
			||||
COPY --from=debian /app/resources/public /usr/share/nginx/html | 
				
			||||
@ -0,0 +1,11 @@
				@@ -0,0 +1,11 @@
					 | 
				
			||||
pipeline { | 
				
			||||
    agent any | 
				
			||||
    options { | 
				
			||||
        skipStagesAfterUnstable() | 
				
			||||
    } | 
				
			||||
    stages { | 
				
			||||
    	stage('Build') { | 
				
			||||
	    sh 'echo "Hello!"' | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,40 @@
				@@ -0,0 +1,40 @@
					 | 
				
			||||
# pong | 
				
			||||
 | 
				
			||||
FIXME: Write a one-line description of your library/project. | 
				
			||||
 | 
				
			||||
## Overview | 
				
			||||
 | 
				
			||||
FIXME: Write a paragraph about the library/project and highlight its goals. | 
				
			||||
 | 
				
			||||
## Setup | 
				
			||||
 | 
				
			||||
To get an interactive development environment run: | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    lein figwheel | 
				
			||||
 | 
				
			||||
and open your browser at [localhost:3449](http://localhost:3449/). | 
				
			||||
This will auto compile and send all changes to the browser without the | 
				
			||||
need to reload. After the compilation process is complete, you will | 
				
			||||
get a Browser Connected REPL. An easy way to try it is: | 
				
			||||
 | 
				
			||||
    (js/alert "Am I connected?") | 
				
			||||
 | 
				
			||||
and you should see an alert in the browser window. | 
				
			||||
 | 
				
			||||
To clean all compiled files: | 
				
			||||
 | 
				
			||||
    lein clean | 
				
			||||
 | 
				
			||||
To create a production build run: | 
				
			||||
 | 
				
			||||
    lein do clean, cljsbuild once min | 
				
			||||
 | 
				
			||||
And open your browser in `resources/public/index.html`. You will not | 
				
			||||
get live reloading, nor a REPL.  | 
				
			||||
 | 
				
			||||
## License | 
				
			||||
 | 
				
			||||
Copyright © 2014 FIXME | 
				
			||||
 | 
				
			||||
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. | 
				
			||||
@ -0,0 +1,42 @@
				@@ -0,0 +1,42 @@
					 | 
				
			||||
(ns user | 
				
			||||
  (:require | 
				
			||||
   [figwheel-sidecar.repl-api :as f])) | 
				
			||||
 | 
				
			||||
;; user is a namespace that the Clojure runtime looks for and | 
				
			||||
;; loads if its available | 
				
			||||
 | 
				
			||||
;; You can place helper functions in here. This is great for starting | 
				
			||||
;; and stopping your webserver and other development services | 
				
			||||
 | 
				
			||||
;; The definitions in here will be available if you run "lein repl" or launch a | 
				
			||||
;; Clojure repl some other way | 
				
			||||
 | 
				
			||||
;; You have to ensure that the libraries you :require are listed in your dependencies | 
				
			||||
 | 
				
			||||
;; Once you start down this path | 
				
			||||
;; you will probably want to look at | 
				
			||||
;; tools.namespace https://github.com/clojure/tools.namespace | 
				
			||||
;; and Component https://github.com/stuartsierra/component | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defn fig-start | 
				
			||||
  "This starts the figwheel server and watch based auto-compiler." | 
				
			||||
  [] | 
				
			||||
  ;; this call will only work as long as your :cljsbuild and | 
				
			||||
  ;; :figwheel configurations are at the top level of your project.clj | 
				
			||||
  ;; and are not spread across different lein profiles | 
				
			||||
 | 
				
			||||
  ;; otherwise you can pass a configuration into start-figwheel! manually | 
				
			||||
  (f/start-figwheel!)) | 
				
			||||
 | 
				
			||||
(defn fig-stop | 
				
			||||
  "Stop the figwheel server and watch based auto-compiler." | 
				
			||||
  [] | 
				
			||||
  (f/stop-figwheel!)) | 
				
			||||
 | 
				
			||||
;; if you are in an nREPL environment you will need to make sure you | 
				
			||||
;; have setup piggieback for this to work | 
				
			||||
(defn cljs-repl | 
				
			||||
  "Launch a ClojureScript REPL that is connected to your build and host environment." | 
				
			||||
  [] | 
				
			||||
  (f/cljs-repl)) | 
				
			||||
@ -0,0 +1,95 @@
				@@ -0,0 +1,95 @@
					 | 
				
			||||
(defproject pong "0.1.0-SNAPSHOT" | 
				
			||||
  :description "FIXME: write this!" | 
				
			||||
  :url "http://example.com/FIXME" | 
				
			||||
  :license {:name "Eclipse Public License" | 
				
			||||
            :url "http://www.eclipse.org/legal/epl-v10.html"} | 
				
			||||
 | 
				
			||||
  :min-lein-version "2.9.1" | 
				
			||||
 | 
				
			||||
  :dependencies [[org.clojure/clojure "1.10.0"] | 
				
			||||
                 [org.clojure/clojurescript "1.10.773"] | 
				
			||||
                 [org.clojure/core.async  "0.4.500"]] | 
				
			||||
 | 
				
			||||
  :plugins [[lein-figwheel "0.5.20"] | 
				
			||||
            [lein-cljsbuild "1.1.7" :exclusions [[org.clojure/clojure]]]] | 
				
			||||
 | 
				
			||||
  :source-paths ["src"] | 
				
			||||
 | 
				
			||||
  :cljsbuild {:builds | 
				
			||||
              [{:id "dev" | 
				
			||||
                :source-paths ["src"] | 
				
			||||
 | 
				
			||||
                ;; The presence of a :figwheel configuration here | 
				
			||||
                ;; will cause figwheel to inject the figwheel client | 
				
			||||
                ;; into your build | 
				
			||||
                :figwheel {:on-jsload "pong.core/on-js-reload" | 
				
			||||
                           ;; :open-urls will pop open your application | 
				
			||||
                           ;; in the default browser once Figwheel has | 
				
			||||
                           ;; started and compiled your application. | 
				
			||||
                           ;; Comment this out once it no longer serves you. | 
				
			||||
                           :open-urls ["http://localhost:3449/index.html"]} | 
				
			||||
 | 
				
			||||
                :compiler {:main pong.core | 
				
			||||
                           :asset-path "js/compiled/out" | 
				
			||||
                           :output-to "resources/public/js/compiled/pong.js" | 
				
			||||
                           :output-dir "resources/public/js/compiled/out" | 
				
			||||
                           :source-map-timestamp true | 
				
			||||
                           ;; To console.log CLJS data-structures make sure you enable devtools in Chrome | 
				
			||||
                           ;; https://github.com/binaryage/cljs-devtools | 
				
			||||
                           :preloads [devtools.preload]}} | 
				
			||||
               ;; This next build is a compressed minified build for | 
				
			||||
               ;; production. You can build this with: | 
				
			||||
               ;; lein cljsbuild once min | 
				
			||||
               {:id "min" | 
				
			||||
                :source-paths ["src"] | 
				
			||||
                :compiler {:output-to "resources/public/js/compiled/pong.js" | 
				
			||||
                           :main pong.core | 
				
			||||
                           :optimizations :advanced | 
				
			||||
                           :pretty-print false}}]} | 
				
			||||
 | 
				
			||||
  :figwheel {;; :http-server-root "public" ;; default and assumes "resources" | 
				
			||||
             ;; :server-port 3449 ;; default | 
				
			||||
             ;; :server-ip "127.0.0.1" | 
				
			||||
 | 
				
			||||
             :css-dirs ["resources/public/css"] ;; watch and update CSS | 
				
			||||
 | 
				
			||||
             ;; Start an nREPL server into the running figwheel process | 
				
			||||
             ;; :nrepl-port 7888 | 
				
			||||
 | 
				
			||||
             ;; Server Ring Handler (optional) | 
				
			||||
             ;; if you want to embed a ring handler into the figwheel http-kit | 
				
			||||
             ;; server, this is for simple ring servers, if this | 
				
			||||
 | 
				
			||||
             ;; doesn't work for you just run your own server :) (see lein-ring) | 
				
			||||
 | 
				
			||||
             ;; :ring-handler hello_world.server/handler | 
				
			||||
 | 
				
			||||
             ;; To be able to open files in your editor from the heads up display | 
				
			||||
             ;; you will need to put a script on your path. | 
				
			||||
             ;; that script will have to take a file path and a line number | 
				
			||||
             ;; ie. in  ~/bin/myfile-opener | 
				
			||||
             ;; #! /bin/sh | 
				
			||||
             ;; emacsclient -n +$2 $1 | 
				
			||||
             ;; | 
				
			||||
             ;; :open-file-command "myfile-opener" | 
				
			||||
 | 
				
			||||
             ;; if you are using emacsclient you can just use | 
				
			||||
             ;; :open-file-command "emacsclient" | 
				
			||||
 | 
				
			||||
             ;; if you want to disable the REPL | 
				
			||||
             ;; :repl false | 
				
			||||
 | 
				
			||||
             ;; to configure a different figwheel logfile path | 
				
			||||
             ;; :server-logfile "tmp/logs/figwheel-logfile.log" | 
				
			||||
 | 
				
			||||
             ;; to pipe all the output to the repl | 
				
			||||
             ;; :server-logfile false | 
				
			||||
             } | 
				
			||||
 | 
				
			||||
  :profiles {:dev {:dependencies [[binaryage/devtools "1.0.0"] | 
				
			||||
                                  [figwheel-sidecar "0.5.20"]] | 
				
			||||
                   ;; need to add dev source path here to get user.clj loaded | 
				
			||||
                   :source-paths ["src" "dev"] | 
				
			||||
                   ;; need to add the compiled assets to the :clean-targets | 
				
			||||
                   :clean-targets ^{:protect false} ["resources/public/js/compiled" | 
				
			||||
                                                     :target-path]}}) | 
				
			||||
@ -0,0 +1,8 @@
				@@ -0,0 +1,8 @@
					 | 
				
			||||
/* some style */ | 
				
			||||
 | 
				
			||||
body { | 
				
			||||
  margin: 0; | 
				
			||||
  display: flex; | 
				
			||||
  background-color: black; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
| 
		 After Width: | Height: | Size: 2.9 KiB  | 
@ -0,0 +1,13 @@
				@@ -0,0 +1,13 @@
					 | 
				
			||||
<!DOCTYPE html> | 
				
			||||
<html> | 
				
			||||
  <head> | 
				
			||||
    <meta charset="UTF-8"> | 
				
			||||
    <meta name="viewport" content="width=device-width, initial-scale=1"> | 
				
			||||
    <link href="css/style.css" rel="stylesheet" type="text/css"> | 
				
			||||
    <link rel="icon" href="favicon.ico"> | 
				
			||||
  </head> | 
				
			||||
  <body> | 
				
			||||
    <canvas id="game" width="200" height="100"></canvas> | 
				
			||||
    <script src="js/compiled/pong.js" type="text/javascript"></script> | 
				
			||||
  </body> | 
				
			||||
</html> | 
				
			||||
@ -0,0 +1,371 @@
				@@ -0,0 +1,371 @@
					 | 
				
			||||
(ns pong.core | 
				
			||||
  (:require [pong.menu :as menu])) | 
				
			||||
 | 
				
			||||
(enable-console-print!) | 
				
			||||
 | 
				
			||||
;; define your app data so that it doesn't get over-written on reload | 
				
			||||
 | 
				
			||||
(declare game-draw next-frame relaunch-ball game-loop) | 
				
			||||
 | 
				
			||||
(defonce keyboard (atom {})) | 
				
			||||
 | 
				
			||||
(def canvas (.getElementById js/document "game")) | 
				
			||||
(def context (.getContext canvas "2d")) | 
				
			||||
(def paddle-height 320) | 
				
			||||
(def paddle-width 40) | 
				
			||||
(def paddle-speed 1) | 
				
			||||
(def ball-radius 20) | 
				
			||||
(def ball-speed 0.75) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defn window-height | 
				
			||||
  [] | 
				
			||||
  (.-innerHeight js/window)) | 
				
			||||
 | 
				
			||||
(defn window-width | 
				
			||||
  [] | 
				
			||||
  (.-innerWidth js/window)) | 
				
			||||
 | 
				
			||||
(defn unit-vector | 
				
			||||
  [v] | 
				
			||||
  (let [length (Math.sqrt (reduce + (map #(Math.pow % 2) v)))] | 
				
			||||
    (map #(/ % length) v))) | 
				
			||||
 | 
				
			||||
(defn contains-point? | 
				
			||||
  [rect x y] | 
				
			||||
  (and (> x (:x rect)) | 
				
			||||
       (< x (+ (:x rect) (:w rect))) | 
				
			||||
       (> y (:y rect)) | 
				
			||||
       (< y (+ (:y rect) (:h rect))))) | 
				
			||||
 | 
				
			||||
(defn collision? | 
				
			||||
  [ball paddle] | 
				
			||||
  (or (contains-point? paddle (- (:x ball) (:radius ball)) (:y ball)) | 
				
			||||
      (contains-point? paddle (+ (:x ball) (:radius ball)) (:y ball)))) | 
				
			||||
 | 
				
			||||
(defn apply-ball-velocity | 
				
			||||
  [{:keys [ball progress] :as state}] | 
				
			||||
  (let [velocity (map #(* % progress) (:velocity ball))] | 
				
			||||
    (assoc state :ball | 
				
			||||
           (assoc ball | 
				
			||||
                  :x (+ (first velocity) (:x ball)) | 
				
			||||
                  :y (+ (second velocity) (:y ball)))))) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-top-wall | 
				
			||||
  [{{y :y radius :radius [vx vy] :velocity} :ball :as state}] | 
				
			||||
  (if (< (- y radius) 0) | 
				
			||||
    (as-> state state | 
				
			||||
        (assoc-in state [:ball :y] radius) | 
				
			||||
        (assoc-in state [:ball :velocity] [vx (- vy)])) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-bottom-wall | 
				
			||||
  [{{y :y radius :radius [vx vy] :velocity} :ball :as state}] | 
				
			||||
  (if (> (+ y radius) (window-height)) | 
				
			||||
    (as-> state state | 
				
			||||
        (assoc-in state [:ball :y] (- (window-height) radius)) | 
				
			||||
        (assoc-in state [:ball :velocity] [vx (- vy)])) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-paddle | 
				
			||||
  [{ball-y :y [dx] :velocity} {paddle-y :y paddle-h :h}] | 
				
			||||
  (unit-vector [(* (- (/ dx (Math.abs dx))) (window-width)) | 
				
			||||
                (* (- (/ (- ball-y paddle-y) paddle-h) 0.5) (window-height))])) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-left-paddle | 
				
			||||
  [{paddle :left-paddle ball :ball :as state}] | 
				
			||||
  (assoc-in state [:ball :velocity] (bounce-ball-off-paddle ball paddle))) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-right-paddle | 
				
			||||
  [{paddle :right-paddle ball :ball :as state}] | 
				
			||||
  (assoc-in state [:ball :velocity] (bounce-ball-off-paddle ball paddle))) | 
				
			||||
 | 
				
			||||
(defn put-ball-infront-of-left-paddle | 
				
			||||
  [{{:keys [radius]} :ball {:keys [x w]} :left-paddle :as state}] | 
				
			||||
  (assoc-in state [:ball :x] (+ x w radius))) | 
				
			||||
 | 
				
			||||
(defn put-ball-infront-of-right-paddle | 
				
			||||
  [{{:keys [radius]} :ball {:keys [x w]} :right-paddle :as state}] | 
				
			||||
  (assoc-in state [:ball :x] (- x radius))) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-left-paddle? | 
				
			||||
  [{:keys [ball left-paddle] :as state}] | 
				
			||||
  (if (collision? ball left-paddle) | 
				
			||||
    (-> state | 
				
			||||
        put-ball-infront-of-left-paddle | 
				
			||||
        bounce-ball-off-left-paddle) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn bounce-ball-off-right-paddle? | 
				
			||||
  [{:keys [ball right-paddle] :as state}] | 
				
			||||
  (if (collision? ball right-paddle) | 
				
			||||
    (-> state | 
				
			||||
        put-ball-infront-of-right-paddle | 
				
			||||
        bounce-ball-off-right-paddle) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn launch-ball-left | 
				
			||||
  [state] | 
				
			||||
  (assoc state :ball (relaunch-ball -1))) | 
				
			||||
 | 
				
			||||
(defn launch-ball-right | 
				
			||||
  [state] | 
				
			||||
  (assoc state :ball (relaunch-ball 1))) | 
				
			||||
 | 
				
			||||
(defn increase-right-player-score | 
				
			||||
  [state] | 
				
			||||
  (update-in state [:score :right] inc)) | 
				
			||||
 | 
				
			||||
(defn increase-left-player-score | 
				
			||||
  [state] | 
				
			||||
  (update-in state [:score :left] inc)) | 
				
			||||
 | 
				
			||||
(defn left-player-scored | 
				
			||||
  [{{:keys [x radius]} :ball :as state}] | 
				
			||||
  (if (> (- x radius) (window-width)) | 
				
			||||
    (-> state | 
				
			||||
        launch-ball-right | 
				
			||||
        increase-left-player-score) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn right-player-scored | 
				
			||||
  [{{:keys [x radius]} :ball :as state}] | 
				
			||||
  (if (< (+ x radius) 0) | 
				
			||||
    (-> state | 
				
			||||
        launch-ball-left | 
				
			||||
        increase-right-player-score) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn apply-paddle-speed | 
				
			||||
  [paddle direction progress] | 
				
			||||
  (direction (:y paddle) (* paddle-speed progress))) | 
				
			||||
 | 
				
			||||
(defn handle-left-player-paddle-up-movement | 
				
			||||
  [{:keys [progress left-paddle] :as state}] | 
				
			||||
  (if (:KeyW @keyboard) | 
				
			||||
    (assoc-in state | 
				
			||||
              [:left-paddle :y] | 
				
			||||
              (apply-paddle-speed left-paddle - progress)) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn handle-left-player-paddle-down-movement | 
				
			||||
  [{:keys [progress left-paddle] :as state}] | 
				
			||||
  (if (:KeyS @keyboard) | 
				
			||||
    (assoc-in state | 
				
			||||
              [:left-paddle :y] | 
				
			||||
              (apply-paddle-speed left-paddle + progress)) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn keep-left-paddle-in-bounds | 
				
			||||
  [{:keys [left-paddle] :as state}] | 
				
			||||
  (assoc-in state | 
				
			||||
            [:left-paddle :y] | 
				
			||||
            (max 0 (min (- (window-height) (:h left-paddle)) | 
				
			||||
                        (:y left-paddle))))) | 
				
			||||
 | 
				
			||||
(defn update-left-paddle | 
				
			||||
  [{:keys [progress left-paddle] :as state}] | 
				
			||||
  (-> state | 
				
			||||
      handle-left-player-paddle-down-movement | 
				
			||||
      handle-left-player-paddle-up-movement | 
				
			||||
      keep-left-paddle-in-bounds)) | 
				
			||||
 | 
				
			||||
(defn stick-right-paddle-to-right-of-screen | 
				
			||||
  [{:keys [right-paddle] :as state}] | 
				
			||||
  (assoc-in state [:right-paddle :x] (- (window-width) (:w right-paddle)))) | 
				
			||||
 | 
				
			||||
(defn handle-right-player-paddle-up-movement | 
				
			||||
  [{:keys [progress right-paddle] :as state}] | 
				
			||||
  (if (:ArrowUp @keyboard) | 
				
			||||
    (assoc-in state | 
				
			||||
              [:right-paddle :y] | 
				
			||||
              (apply-paddle-speed right-paddle - progress)) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn handle-right-player-paddle-down-movement | 
				
			||||
  [{:keys [progress right-paddle] :as state}] | 
				
			||||
  (if (:ArrowDown @keyboard) | 
				
			||||
    (assoc-in state | 
				
			||||
              [:right-paddle :y] | 
				
			||||
              (apply-paddle-speed right-paddle + progress)) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn player-update-right-paddle | 
				
			||||
  [state] | 
				
			||||
  (-> state | 
				
			||||
      handle-right-player-paddle-up-movement | 
				
			||||
      handle-right-player-paddle-down-movement)) | 
				
			||||
 | 
				
			||||
(defn computer-update-right-paddle | 
				
			||||
  [{:keys [progress right-paddle ball] :as state}] | 
				
			||||
  (let [paddle-middle (+ (:y right-paddle) (/ (:h right-paddle) 2)) | 
				
			||||
        speed (* paddle-speed progress)] | 
				
			||||
    (assoc-in state [:right-paddle :y] | 
				
			||||
              (cond | 
				
			||||
                (< (:y ball) (- paddle-middle speed)) | 
				
			||||
                (max (- (:y right-paddle) speed) 0) | 
				
			||||
                (> (:y ball) (+ paddle-middle speed)) | 
				
			||||
                (min (+ (:y right-paddle) speed) | 
				
			||||
                     (- (window-height) (:h right-paddle))) | 
				
			||||
                :else (:y right-paddle))))) | 
				
			||||
 | 
				
			||||
(defn draw-rect | 
				
			||||
  [{x :x y :y w :w h :h}] | 
				
			||||
  (.fillRect context x y w h)) | 
				
			||||
 | 
				
			||||
(defn fill-circle | 
				
			||||
  [{x :x y :y radius :radius}] | 
				
			||||
  (.beginPath context) | 
				
			||||
  (.arc context x y radius 0 (* 2 Math.PI)) | 
				
			||||
  (.fill context)) | 
				
			||||
 | 
				
			||||
(defn draw-score | 
				
			||||
  [{:keys [left right]}] | 
				
			||||
  (set! (.-font context) "30px monospace") | 
				
			||||
  (.fillText context (str left " | " right) (/ (window-width) 2) 30)) | 
				
			||||
 | 
				
			||||
(defn game-draw | 
				
			||||
  [{:keys [left-paddle right-paddle ball score] :as state}] | 
				
			||||
  (.clearRect context 0 0 (window-width) (window-height)) | 
				
			||||
  (set! (.-fillStyle context) "#FFF") | 
				
			||||
  (draw-rect left-paddle) | 
				
			||||
  (draw-rect right-paddle) | 
				
			||||
  (fill-circle ball) | 
				
			||||
  (draw-score score) | 
				
			||||
  state) | 
				
			||||
 | 
				
			||||
(defn handle-return-to-menu | 
				
			||||
  [{:keys [keyboard] :as state}] | 
				
			||||
  (if (:Escape keyboard) | 
				
			||||
    (assoc state :on-loop menu/scene) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn game-loop | 
				
			||||
  [{:keys [update-right-paddle] :as state}] | 
				
			||||
  (-> state | 
				
			||||
      update-left-paddle | 
				
			||||
      update-right-paddle | 
				
			||||
      stick-right-paddle-to-right-of-screen | 
				
			||||
      apply-ball-velocity | 
				
			||||
      bounce-ball-off-top-wall | 
				
			||||
      bounce-ball-off-bottom-wall | 
				
			||||
      bounce-ball-off-left-paddle? | 
				
			||||
      bounce-ball-off-right-paddle? | 
				
			||||
      left-player-scored | 
				
			||||
      right-player-scored | 
				
			||||
      handle-return-to-menu | 
				
			||||
      game-draw)) | 
				
			||||
 | 
				
			||||
(defn next-frame | 
				
			||||
  [last-render state] | 
				
			||||
  (.requestAnimationFrame js/window | 
				
			||||
                          #(next-frame % ((:on-loop state) | 
				
			||||
                                          (merge state | 
				
			||||
                                                 {:window-width (.-innerWidth js/window) | 
				
			||||
                                                  :window-height (.-innerHeight js/window) | 
				
			||||
                                                  :keyboard @keyboard | 
				
			||||
                                                  :progress (- % last-render)}))))) | 
				
			||||
 | 
				
			||||
(defn vec* | 
				
			||||
  [v d] | 
				
			||||
  (map #(* % d) v)) | 
				
			||||
 | 
				
			||||
(defn one-or-minus-one | 
				
			||||
  [] | 
				
			||||
  (let [n (- (rand 2) 1)] (/ n (Math.abs n)))) | 
				
			||||
 | 
				
			||||
(defn launch-ball [x y dx dy] | 
				
			||||
  {:x x | 
				
			||||
   :y y | 
				
			||||
   :radius ball-radius | 
				
			||||
   :velocity (vec* (unit-vector [dx dy]) ball-speed)}) | 
				
			||||
 | 
				
			||||
(defn relaunch-ball | 
				
			||||
  [direction] | 
				
			||||
  (launch-ball (/ (window-width) 2) | 
				
			||||
               (rand (window-height)) | 
				
			||||
               (* direction (/ (window-width) 2)) | 
				
			||||
               (- (rand (window-height)) (/ (window-height) 2)))) | 
				
			||||
 | 
				
			||||
(defn start-game | 
				
			||||
  [state update-right-paddle] | 
				
			||||
  (merge state | 
				
			||||
         {:left-paddle {:x 0.0 | 
				
			||||
                        :y (/ (- (window-height) paddle-height) 2) | 
				
			||||
                        :w paddle-width | 
				
			||||
                        :h paddle-height} | 
				
			||||
          :right-paddle {:x (- (window-width) paddle-width) | 
				
			||||
                         :y (/ (- (window-height) paddle-height) 2) | 
				
			||||
                         :w paddle-width | 
				
			||||
                         :h paddle-height} | 
				
			||||
          :update-right-paddle update-right-paddle | 
				
			||||
          :ball (relaunch-ball (one-or-minus-one)) | 
				
			||||
          :score {:left 0 :right 0} | 
				
			||||
          :on-loop game-loop})) | 
				
			||||
 | 
				
			||||
(defn start-one-player-game | 
				
			||||
  [state] | 
				
			||||
  (println "start one player game") | 
				
			||||
  (start-game state computer-update-right-paddle)) | 
				
			||||
 | 
				
			||||
(defn start-two-players-game | 
				
			||||
  [state] | 
				
			||||
  (println "start two players game") | 
				
			||||
  (start-game state player-update-right-paddle)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(next-frame (.now js/performance) | 
				
			||||
            {:on-loop menu/scene | 
				
			||||
             :menu-item-selected 0 | 
				
			||||
             :menu-items [{:txt "1 Player" | 
				
			||||
                           :action start-one-player-game} | 
				
			||||
                          {:txt "2 Players" | 
				
			||||
                           :action start-two-players-game}] | 
				
			||||
             :context context}) | 
				
			||||
 | 
				
			||||
(defn set-canvas-size-to-window-size | 
				
			||||
  [] | 
				
			||||
  (set! (.-width canvas) (.-innerWidth js/window)) | 
				
			||||
  (set! (.-height canvas) (.-innerHeight js/window))) | 
				
			||||
 | 
				
			||||
(set-canvas-size-to-window-size) | 
				
			||||
 | 
				
			||||
(set! (.-onresize js/window) | 
				
			||||
      set-canvas-size-to-window-size) | 
				
			||||
 | 
				
			||||
;; (def update-game-state (partial swap! game-state assoc)) | 
				
			||||
;; (def update-left-paddle-movement (partial update-game-state :left-paddle-movement)) | 
				
			||||
;; (def update-right-paddle-movement (partial update-game-state :right-paddle-movement)) | 
				
			||||
 | 
				
			||||
;; (swap! game-state assoc :on-loop menu-loop) | 
				
			||||
;; (fn [event] | 
				
			||||
;;                      (let [code (.-code event)] | 
				
			||||
;;                        (case code | 
				
			||||
;;                          "KeyW" (update-left-paddle-movement :up) | 
				
			||||
;;                          "KeyS" (update-left-paddle-movement :down) | 
				
			||||
;;                          "ArrowUp" (update-right-paddle-movement :up) | 
				
			||||
;;                          "ArrowDown" (update-right-paddle-movement :down) | 
				
			||||
;;                          "Escape" (swap! game-state assoc :on-loop menu-loop) | 
				
			||||
;;                          nil))) | 
				
			||||
 | 
				
			||||
;; (fn [event] | 
				
			||||
;;                      (let [code (.-code event)] | 
				
			||||
;;                        (case code | 
				
			||||
;;                          "KeyW" (and (= (:left-paddle-movement @game-state) :up) | 
				
			||||
;;                                      (update-left-paddle-movement nil)) | 
				
			||||
;;                          "KeyS" (and (= (:left-paddle-movement @game-state) :down) | 
				
			||||
;;                                      (update-left-paddle-movement nil)) | 
				
			||||
;;                          "ArrowUp" (and (= (:right-paddle-movement @game-state) :up) | 
				
			||||
;;                                      (update-right-paddle-movement nil)) | 
				
			||||
;;                          "ArrowDown" (and (= (:right-paddle-movement @game-state) :down) | 
				
			||||
;;                                           (update-right-paddle-movement nil)) | 
				
			||||
;;                          nil))) | 
				
			||||
 | 
				
			||||
(.addEventListener js/document "keydown" #(swap! keyboard assoc (keyword (.-code %)) %)) | 
				
			||||
(.addEventListener js/document "keyup" #(swap! keyboard dissoc (keyword (.-code %)))) | 
				
			||||
 | 
				
			||||
(defn on-js-reload [] | 
				
			||||
  ;; optionally touch your app-state to force rerendering depending on | 
				
			||||
  ;; your application | 
				
			||||
  ;; (swap! app-state update-in [:__figwheel_counter] inc) | 
				
			||||
) | 
				
			||||
@ -0,0 +1,78 @@
				@@ -0,0 +1,78 @@
					 | 
				
			||||
(ns pong.menu) | 
				
			||||
 | 
				
			||||
(def next-key-ms 200) | 
				
			||||
 | 
				
			||||
(defn decrease-next-key | 
				
			||||
  [{:keys [next-key progress] :as state}] | 
				
			||||
  (if (> next-key 0) | 
				
			||||
    (assoc state :next-key (- next-key progress)) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn handle-menu-up | 
				
			||||
  [{:keys [menu-item-selected menu-items keyboard next-key] :as state}] | 
				
			||||
  (if (and (or (:KeyW keyboard) (:ArrowUp keyboard)) (<= next-key 0)) | 
				
			||||
    (merge state | 
				
			||||
           {:menu-item-selected (mod (dec (+ menu-item-selected (count menu-items))) | 
				
			||||
                                     (count menu-items)) | 
				
			||||
            :next-key next-key-ms}) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn handle-menu-down | 
				
			||||
  [{:keys [menu-item-selected menu-items keyboard next-key] :as state}] | 
				
			||||
  (if (and (or (:KeyS keyboard) (:ArrowDown keyboard)) (<= next-key 0)) | 
				
			||||
    (merge state | 
				
			||||
           {:menu-item-selected (mod (inc menu-item-selected) (count menu-items)) | 
				
			||||
            :next-key next-key-ms}) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn handle-menu-selected | 
				
			||||
  [{:keys [menu-item-selected menu-items keyboard] :as state}] | 
				
			||||
  (if (:Enter keyboard) | 
				
			||||
    ((:action (nth menu-items menu-item-selected)) | 
				
			||||
             state) | 
				
			||||
    state)) | 
				
			||||
 | 
				
			||||
(defn draw-text-centered | 
				
			||||
  [context txt x y] | 
				
			||||
  (let [width (.-width (.measureText context txt))] | 
				
			||||
    (.fillText context txt (- x (/ width 2)) y))) | 
				
			||||
 | 
				
			||||
(defn draw-menu-item | 
				
			||||
  [context item selected x y] | 
				
			||||
  (draw-text-centered context | 
				
			||||
                      (str (when selected ">") | 
				
			||||
                           (:txt item) | 
				
			||||
                           (when selected "<")) | 
				
			||||
                      x | 
				
			||||
                      y)) | 
				
			||||
 | 
				
			||||
(defn draw | 
				
			||||
  [{:keys [context menu-item-selected window-width window-height menu-items] :as state}] | 
				
			||||
  (.clearRect context 0 0 | 
				
			||||
              window-width | 
				
			||||
              window-height) | 
				
			||||
 | 
				
			||||
  (set! (.-fillStyle context) "#FFF") | 
				
			||||
  (set! (.-font context) "30px monospace") | 
				
			||||
 | 
				
			||||
  (draw-text-centered context "Pariatech's Pong" (/ window-width 2) 60) | 
				
			||||
   | 
				
			||||
 | 
				
			||||
  (let [middle-horizontally (/ window-width 2) | 
				
			||||
        offset-vertically (/ (- window-height (* (count menu-items) 40)) 2)] | 
				
			||||
    (doall (map-indexed #(draw-menu-item context | 
				
			||||
                                         %2 | 
				
			||||
                                         (= %1 (Math/floor menu-item-selected)) | 
				
			||||
                                         middle-horizontally | 
				
			||||
                                         (+ offset-vertically (* %1 40))) | 
				
			||||
                        menu-items))) | 
				
			||||
  state) | 
				
			||||
 | 
				
			||||
(defn scene | 
				
			||||
  [state] | 
				
			||||
  (-> state  | 
				
			||||
      decrease-next-key | 
				
			||||
      handle-menu-up | 
				
			||||
      handle-menu-down | 
				
			||||
      handle-menu-selected | 
				
			||||
      draw)) | 
				
			||||
					Loading…
					
					
				
		Reference in new issue