August 12, 2018
4clojure 138 - Squares Squared
Create a function of two integer arguments: the start and end, respectively. You must create a vector of strings which renders a 45° rotated square of integers which are successive squares from the start point up to and including the end point. If a number comprises multiple digits, wrap them around the shape individually. If there are not enough digits to complete the shape, fill in the rest with asterisk characters. The direction of the drawing should be clockwise, starting from the center of the shape and working outwards, with the initial direction being down and to the right.
(ns live.test
(:require [cljs.test :refer-macros [deftest is run-tests]]))
jafingerhut's solution:
;; Create the sequence of numbers, convert them to a sequence of
;; characters, and pad it with * characters if needed to make the
;; total length a square number.
;; Create the spiral pattern in a rectangular grid where the first
;; digit starts at coordinates (0,0), then goes right-down to (1,-1),
;; then left-down to (0,-2), left-up to (-1,-1), etc. Use a map that
;; maps coordinates (represented as two-element vectors) to the
;; character at that position, so that it is easy to grow the size of
;; it as needed on demand.
;; Then convert it to the vector of strings needed for the correct
;; return value.
(defn squares [start end]
(let [numbers (take-while #(<= % end) (iterate #(*' % %) start))
to-digits (fn [n] (map #(- (int %) (int \0)) (str n)))
digits (mapcat to-digits numbers)
n (count digits)
next-square-number (first (filter #(>= % n)
(map #(*' % %) (range))))
digits (concat digits (repeat (- next-square-number n) \*))
numsteps (interleave (drop 1 (range)) (drop 1 (range)))
dirs (cycle [[1 -1] [-1 -1] [-1 1] [1 1]])
deltas (mapcat #(repeat % %2) numsteps dirs)
positions (take next-square-number
(reductions (fn [[x y] [dx dy]] [(+ x dx) (+ y dy)])
[0 0] deltas))
char-positions (into {} (map (fn [p d] [p d]) positions digits))
x-coords (map first positions)
min-x (apply min x-coords)
max-x (apply max x-coords)
y-coords (map second positions)
min-y (apply min y-coords)
max-y (apply max y-coords)]
(for [y (range max-y (dec min-y) -1)]
(apply str
(for [x (range min-x (inc max-x))]
(get char-positions [x y] " "))))))
(deftest test-137
(is (= (squares 2 2) ["2"]))
(is (= (squares 2 4) [" 2 "
"* 4"
" * "]))
(is (= (squares 3 81) [" 3 "
"1 9"
" 8 "]))
(is (= (squares 4 20) [" 4 "
"* 1"
" 6 "]))
(is (= (squares 2 256) [" 6 "
" 5 * "
"2 2 *"
" 6 4 "
" 1 "]))
(is (= (squares 10 10000) [" 0 "
" 1 0 "
" 0 1 0 "
"* 0 0 0"
" * 1 * "
" * * "
" * "])))
(run-tests)