| Bytes | Lang | Time | Link |
|---|---|---|---|
| 075 | Ruby rio/console | 240919T200321Z | Jordan |
| 423 | TurboWarp | 240921T121932Z | Rhaixer |
| 088 | C Turbo C | 240919T150244Z | jdt |
| 403 | Rust | 240921T115311Z | user9403 |
| 123 | Nim | 240923T090113Z | janAkali |
| 040 | x86 .COM opcode | 240919T071100Z | l4m2 |
| 076 | HTML | 240919T082944Z | Kevin Cr |
| nan | 240922T173205Z | A.J Serv | |
| 101 | Python | 240920T162502Z | William |
| 080 | R + cli | 240921T104256Z | Dominic |
| 212 | Java 21 | 240921T115556Z | Juuz |
| 083 | QBasic | 240919T182446Z | DLosc |
| 087 | Zsh | 240919T132850Z | GammaFun |
| 121 | ZX Spectrum 48K/128K Basic | 240919T164810Z | Neil |
Ruby -rio/console, 79 75 bytes
Contains unprintable characters. (See ungolfed code).
i=0
(c=STDIN.getch
$><<c==??i>0&&(i-=1
" "):/\d/=~c&&(i+=1
?*))while i<4
Ungolfed
i = 0
(
c = STDIN.getch
$> << c == "\x7f" ?
i > 0 && (
i -= 1
"\x08 \x08"
) :
/\d/ =~ c && (
i += 1
"*"
)
) while i < 4
TurboWarp, 424 423 bytes
Key-presses are really hard to manage well.
NOTE: This will not work in Scratch, because backspace is only detectable in TurboWarp.
NOTE: In the scratchblocks version and the picture, I used a custom block containing 1 argument. This was because I had to actually use the block rather than leave it as a function, and scratchblocks needs something in a custom block to recognize it as one. The project linked to will use an empty block, however.
edit: translating into scratchblocks is HARD
define(
set[i v]to(0
repeat until<(i)>(9
if<key(i)pressed?>then
set[x v]to(join(x)[*
stop[this script v
end
change[i v]by(1
end
when gf clicked
set[x v]to[
repeat until<(length of(x))=(4
if<<key(any v)pressed?>and<not<key(backspace v)pressed?>>>then
(
end
if<key(backspace v)pressed?>then
set[j v]to((length of(x))-(2
set[x v]to[
repeat until<(length of(x))>(j
set[x v]to(join(x)[*
end
end
wait until<not<key(any v)pressed?
C (Turbo C), 89 88 bytes
main(n,c){for(;write(1,"\r \r****",(c-8?c>47&c<58?++n:n:n>1?--n:n)+5)<10;)c=getch();}
Rust, 484 468 458 426 403 bytes
use std::io::{stdin,stdout,Write};use termion::{clear,cursor::Left,event::Key::{Backspace,Char},input::TermRead,raw::IntoRawMode};fn main(){let(mut o,mut l)=(stdout().into_raw_mode().unwrap(),0);for k in stdin().keys(){match k.unwrap(){Char(c)=>{if c.is_digit(10){write!(o,"*").unwrap();l+=1}}Backspace=>{write!(o,"{}{}",Left(1),clear::AfterCursor).unwrap();l-=1}_=>()}o.flush().unwrap();if l>3{break}}}
This solution uses Termion, and so doesn't support Windows.
I'm new to terminal manipulation so this probably isn't the most concise way to do this.
Ungolfed:
use std::io::{stdin, stdout, Write};
use termion::{
clear,
cursor::Left,
event::Key::{Backspace, Char},
input::TermRead,
raw::IntoRawMode,
};
fn main() {
let (mut o, mut l) = (stdout().into_raw_mode().unwrap(), 0);
for k in stdin().keys() {
match k.unwrap() {
Char(c) => {
if c.is_digit(10) {
write!(o, "*").unwrap();
l += 1
}
}
Backspace => {
write!(o, "{}{}", Left(1), clear::AfterCursor).unwrap();
l -= 1
}
_ => (),
}
o.flush().unwrap();
if l > 3 {
break;
}
}
}
Nim, 123 bytes
Includes non-printable chars:
import terminal;let w=writeStyled
var n=0;while n<4:n+=(case getch()
of'0'..'9':(w"*";1)
of'':(w"[D[J";-int n>0)
else:0)
Version without non-printable chars, 129 bytes:
import terminal;let w=writeStyled
var n=0;while n<4:n+=(case getch()
of'0'..'9':(w"*";1)
of'\x7F':(w "\e[D\e[J";-int n>0)
else:0)
x86 .COM opcode, 40 bytes
Not placing on left-to corner saves: https://i.sstatic.net/GsmDekFQ.png
org 100h
mov bx, 0AE00h
mov ds, bx
f: mov ah, 0
int 16h
cmp al, 8
je bksp
sub al, '0'
cmp al, 10
jnb f
mov [bx], byte '*'
t: inc bx
inc bx
cmp bl, 8
jb f
ret
bksp:
sub bl, 2
jc t
mov [bx], byte ' '
jmp f
00000000: bb00 ae8e dbb4 00cd 163c 0874 112c 303c .........<.t.,0<
00000010: 0a73 f2c6 072a 4343 80fb 0872 e8c3 80eb .s...*CC...r....
00000020: 0272 f3c6 0720 ebdd .r... ..
HTML, 78 bytes
<input onkeydown=return!value[3]&&(c=event.which^48)-56&&![value+=9<c?'':'*']>
From Kevin Cruijssen's solution, doesn't handle Shift well
HTML, 150 148 134 100 94 84 80 77 76 bytes
<input type=password onkeydown=c=event.key;return!(value[3]||c[5]!='p'&!++c)
-54 bytes thanks to @Shaggy.
-10 bytes thanks to @l4m2 and @jdt.
-4 bytes thanks to @jdt again.
I'm still trying to find a browser that uses asterisk for password-fields instead of bullets (the four browsers I had installed - Chrome; Firefox; Edge; Opera - unfortunately all use bullets). But certain old browser versions defintely used asterisks for password input-fields, so it's assumed this snippet is used in one of those browsers / versions.
Explanation:
<input- Input boxtype=password- Of type password, so every valid character that's typed (aka digits) will be shown as asterisks in certain browsers (in most modern browsers they'll be shown as bullet points instead, though)onkeydown=- Capture and handle key-pressesc=event.key;- Save the keypress as character in variablec, since we'll use it twice later onreturn- Return a boolean:!(...)- By negating the following bits:value[3]- Stop after the fourth digit has been entered||- Otherwise:c[5]!='p'- Ifcis NOT a backspace - The sixth character is a 'p', which only applies to the unprintables 'Backspace' and 'PageUp', of which the second won't input or remove anything anyway&!++c- nor a digit (e.keyrange ['0','9']) - The++e.keywill fail for any non-digit value, becomingNaN.
Known issues:
Shift + digits (akaEDIT: It works again since the 77 bytes version that uses!@#$%&*()) seem to work again unfortunately, even though I explicitly fixed that in my initial 150 bytes version.e.keyinstead ofe.whichto check whether it's a digit.- 'preparation characters' (aka
'"`~^) still seem to work unfortunately (even beyond four characters)..
<input type=password onkeydown=e=event;return!(value[3]||!++e.key&e.which!=8)
https://codegolf.stackexchange.com/questions/275630/enter-a-personal-identification-number/275634#275634
Python, 101 bytes
import sys;s=0
while s<4:
c=sys.stdin.read(1)
s+=c.isdigit()-(c=='\x08')
print("*"*s+' ',end='\r')
This script only works on a terminal set to raw input mode.
On UNIX, you can achieve this using stty raw.
Windows version, 102 bytes
import msvcrt;s=0
while s<4:
c=msvcrt.getch()
s+=c.isdigit()-(c==b'\x08')
print("*"*s+' ',end='\r')
This second version uses msvcrt, which is only available on Windows, but it does not require raw input mode.
R + cli, 91 84 80 bytes
while(F<4){cat(c('\b \b','*')[x<-(nchar(k<-cli::keypress())>8&F)-k%in%0:9]);F=F-x}
(\b represents a single 08 byte in the source code)
The R cli (command line interface) library indicates that keypress() "currently only works at Linux/Unix and OSX terminals, and at the Windows command line", so unfortunately I don't know of an online site where this will run correctly. It runs fine in my OSX terminal.
keypress() returns the character representing the key pressed, or a string representing 'special' keys, of which "backspace" is the only one with >8 characters, so nchar()>8 saves 4 bytes compared to =="backspace" and 3 bytes compared to grepl("ba",).
Java 21, 212 bytes
A graphical solution with AWT. The output is displayed in the window title. At least on Windows 10, you need to resize the window so you can see the title, or look at the taskbar etc.
new java.awt.Frame(){{addKeyListener(new java.awt.event.KeyAdapter(){int j,c;public void keyTyped(java.awt.event.KeyEvent e){c=e.getKeyChar();setTitle("*".repeat(j+=j>3?0:47<c&&c<58?1:j>0&&c<9?-1:0));}});}}::show
The code is a Runnable (or similar) method reference.
Commented
new java.awt.Frame() {{ // Create a new anonymous Frame subclass
addKeyListener(new java.awt.event.KeyAdapter() {
int j, c; // j = title length, c = the current character
public void keyTyped(java.awt.event.KeyEvent e) {
c = e.getKeyChar();
setTitle("*".repeat(
j += j > 3 // if the length is 4, don't modify it
? 0
: 47 < c && c < 58 // if the character is a digit,
? 1 // add one asterisk
: j > 0 && c < 9 // key 8 is the backspace and other
// control chars don't get this event
? -1 // remove an asterisk if there are any
: 0
));
}
});
}}::show // show the frame when called
QBasic, 83 bytes
CLS
1c=ASC(INPUT$(1))
d=d-(d>0)*(c=8)+(c>47)*(c<58)
CLS
?STRING$(d,42)
IF d<4GOTO 1
Try it at Archive.org!
Explanation
CLS
Clear the screen.
1
Line number (used as a GOTO target later).
c = ASC(INPUT$(1))
Read one character from user input, get its ASCII code, and store that in c. Digits are 48-57; backspace is 8.
d = d - (d > 0) * (c = 8) + (c > 47) * (c < 58)
Update d, the number of digits entered so far. (Like all numeric variables in QBasic, it starts out with a value of 0.)
- If
d > 0andc = 8are both truthy (-1), multiply them to get 1 and subtract that fromd. That is, if there are any digits and the user presses backspace, remove one digit. - If
c > 47andc < 58are both truthy (-1), multiply them to get 1 and add that tod. That is, if the user presses a digit key, add one digit.
All other keypresses have no effect on d.
CLS
PRINT STRING$(d, 42)
Clear the screen and print a string of d asterisks (ASCII code 42).
IF d < 4 THEN GOTO 1
If the number does not have four digits yet, go back to line 1 and input another character. Otherwise, the program is over and halts.
The leading CLS is perhaps not necessary. Without it, the screen initially shows the results of the last program run, and it is only cleared once the user presses their first key. Since this includes any key, not just the number & backspace keys, I thought it probably ran afoul of "All other key presses should be ignored," so I included an initial clear-screen just in case.
Zsh, 94 87 bytes
Lost 3 bytes to fix a bug (if first key is backspace, a[1]= makes $a an array). Saved 3 bytes by not using %s in the printf statement, then 7 more by switching from while to for.
a=
for ((;$#a<4;))
{read -sk1 k
case $k {$'\C-?')a[1]=;;[0-9])a+=*;}
printf '\r\e[K'$a}
asterisks=
# $#foo: length of $foo
for ((;$#asterisks < 4;)); {
# -s: silent/noecho, -k 1: one keypress
read -s -k 1 key
case $key {
# backspace: delete first character from asterisks string
$'\C-?') asterisks[1]= ;;
[0-9]) asterisks+=*
}
# \r: start of line, \e[K: clear to end of line
printf '\r\e[K'$asterisks
}
ZX Spectrum 48K/128K Basic, 121 bytes
2 LET a=NOT PI
3 IF INKEY$<>"" THEN GO TO PI
4 LET a$=INKEY$: IF a$="" THE
N GOTO VAL "4"
5 LET a=a+(a$>="0" AND a$<="9
")-(CODE a$=VAL "12" AND a)
6 PRINT AT NOT PI,NOT PI;"***
*"(TO a),
7 IF a<PI THEN GO TO PI
The above is how the program lists on screen as the ZX Spectrum's screen is 32 characters wide. The byte count was calculated by subtracting the system variables for the end and start of the program; this will be one less than the save file length as an empty program has a save file length of 1. Unfortunately I was using an online emulator which only has a save snapshot option, so you'll have to retype this yourself to test it. When typing this in 128K Basic you may notice the editor adding extra spaces but these do not take up any extra bytes, while when typing this in 48K Basic you need to ensure you use all of the tokens including <>, >= and <=. Explanation:
2 LET a=NOT PI
Start with no digits entered. (Literal numbers cost their length plus six bytes so when golfing you need to use any means possible to avoid them.)
3 IF INKEY$<>"" THEN GO TO PI
Wait until the current key is released. (Yes, the line number was chosen so that the GO TO could be as short as possible.)
4 LET a$=INKEY$: IF a$="" THEN GOTO VAL "4"
Wait until a key is pressed, capturing its value in a$.
5 LET a=a+(a$>="0" AND a$<="9")-(CODE a$=VAL "12" AND a)
Adjust the number of digits according to whether the key was a digit or the code for delete (which is what the ZX Spectrum calls backspace). Note that AND evaluates both its arguments and returns the first if the second is nonzero.
6 PRINT AT NOT PI,NOT PI;"****"(TO a),
Output the corresponding number of *s. The , is normally used to jump to the next tab stop, but conveniently it outputs a space first, which will delete the last * if the last key was the delete key.
7 IF a<PI THEN GO TO PI
Repeat until 4 digits have been input. (PI is doing a lot of heavy lifting in this program!)
This program could also be adapted to run on the ZX81 but you would need to split line 4 up into two lines as the ZX81 only accepts one statement per line except in IF...THEN statements. This would increase the byte count by 4.

