Definitely will do that
Thanks for all the encouragement.
Last week I bought 10 arrow cylinders on ebay, all keyed differently. After seeing the $120 set of progressively pinned cylinders at toool.us, I decided I'd use my 10 cylinders (which I bought for *way* less than that shipped) to make myself some training cylinders.
Ten cylinders, all keyed differently - and the key bitting was on the keys. So I typed all the key bittings into a text file and wrote myself some code to count what pins I had available, so I could figure out what to do with them. I decided I'd skip making one pin cylinders, which leaves me with, what do I make? So I added on to the code until it could tell me which pairs of keys I could make two sets of progressively pinned cylinders, each set of five using a different key - and it turned out there were various keys I could use. I chose the two keys that left me with the most extra pins of the pins sizes I was using, just in case.
- Code:
(defvar *keycode-list* nil)
(defvar *available-pins* (make-array 10 :initial-element 0))
(defmacro read-iterate ((varname data-filename) &rest code)
"read-iterate varname data-filename &rest code - loop through the forms in
a file and execute a peice of code. The expanded code will use varname
to hold each form read"
`(with-open-file (data-file ,data-filename :direction :input)
(when data-file
(do ((,varname (read-line data-file nil) (read-line data-file nil)))
((null ,varname) T)
,@code))))
(defun make-ratchet-predicate (n)
"make-ratched-predicate returns a predicate that returns true after it
has been called (abs n) times"
(let ((count (abs n)))
(lambda ()
(or (= 0 count) (not (setf count (1- count)))))))
(defun make-pin-counter (n &optional (minpins 1))
"make-pin-counter returns a function that will return the correct number
of pins needed to make a progressive set of cylinders with each successive
call, assuming that n is the number of pins in the final cylinder and
minpins is the minimum number of pins in the first cylinder"
(let ((predicate (make-ratchet-predicate minpins))
(pincount (- n (1- minpins))))
(lambda ()
(if (funcall predicate)
(setf pincount (1- pincount))
pincount))))
(defun code-to-list (keycode)
"Converts a keycode to a list of integers"
(map 'list #'digit-char-p keycode))
(defun add-all-bittings (keycode &optional (pincounts *available-pins*))
"Adds all bittings from a keycode to an array of available pins"
(map 'nil #'(lambda (pin)
(setf (aref pincounts pin) (1+ (aref pincounts pin))))
(code-to-list keycode)))
(defun leftover-pins (available-pins required-pins)
"Return the pins left over after using required-pins from the stock
of available-pins"
(map 'vector #'- available-pins required-pins))
(defun required-pins (keycode &optional (minpins 1))
"Return the pins required to progressively pin a particular keycode
assuming a minimum of minpins pins used in the first cylinder"
(let ((result (make-array 10 :initial-element 0))
(counter (make-pin-counter (length keycode) minpins)))
(map 'nil #'(lambda (pin)
(setf (aref result pin) (+ (aref result pin)
(funcall counter))))
(code-to-list keycode))
result))
(defun progressive-pins-p (keycode available &optional (minpins 1))
"Return T if it is possible to progressively pin the keycode using
available pins"
(let* ((required (required-pins keycode minpins))
(leftover (leftover-pins available required)))
(values
(not (find-if #'(lambda (value) (< value 0)) leftover))
required
leftover)))
(defun clear-pin-data ()
"Reset special pin data variable"
(setf *available-pins* (make-array 10 :initial-element 0)))
(defun clear-key-data ()
"Reset special key list variable"
(setf *keycode-list* nil))
(defun load-all-data ()
"Load pin data from data file into special pin data variable"
(clear-key-data)
(clear-pin-data)
(read-iterate (keycode "keyinfo.dat")
(push keycode *keycode-list*)
(add-all-bittings keycode))
(setf *keycode-list* (nreverse *keycode-list*)))
(defun keycode-list ()
"Return a list of all key codes from the data file"
*keycode-list*)
(defun progressive-pin-info ()
"reset and load all data, then print out which pairs of keys can be
progressively pinned into 5 cylinders using the available pins"
(labels ((rec-progressive-pin (list available)
(when list
(let ((keycode (first list)))
(multiple-value-bind (possible required leftover)
(progressive-pins-p keycode available 2)
(when possible
(dolist (secondcode (rest list))
(multiple-value-bind (possible required leftover)
(progressive-pins-p secondcode leftover 2)
(when possible
(format t "Can pin ~A and ~A : ~A~%"
keycode secondcode leftover)))))))
(rec-progressive-pin (rest list) available))))
(load-all-data)
(rec-progressive-pin (keycode-list) *available-pins*)))
At this point I took the two cylinders using the keys I wanted (bitted 513432 and 326561 respectively), and set them aside. Since they're already keyed for the keys I want to use, they will be the six-pin cylinders for the sets. I cut off a piece of 1/2" wooden dowel. This morning, I took a hobby knife and hollowed out one end of the dowel (so it would fit over the end of the plug, which had some clips, etc for the tailpiece), took a cylinder, dumped it, and used that cylinder to make sure the dowel would fit through, and that the end I'd hollowed out was the right size, etc. It was a very tight fit to start so I just sanded it a little until it would move through easily but not too easily.
So now I have a homegrown plug follower:
I started dumping the cylinders, and used a playmat to keep track of what pins were what:
Keyed four cylinders, starting with five pins, with the last cylinder getting the front two pins, to one key - then did the same with four cylinders for the other key, leaving me with two five-cylinder progressively pinned training sets (and extra driver pins, key pins, and springs, not shown):
I then promptly picked them (although this took me a bit when it got to 5 and 6 pins - I have, a couple of times, known exactly what the lock state was, telling myself 'set this pin and it opens' and been right, but I am not very good at it yet and it doesn't happen that often, which is part of the reason I'm making myself progressively pinned training sets):
Thanks to everyone in the chat who helped me out as I was figuring out what I was going to do and how to do it. It helped immensely. I figure I'll pick them all over and over, kind of like a guitarist doing scales, until such a time as I feel like I've progressed enough - at which point I'll take one of them and put in security pins.
You do not have the required permissions to view the files attached to this post.