15
\$\begingroup\$

Take an arbitrarily sized string as input. This string represents a baseball plate appearance, in which each character represents a pitch as follows:

  • Strike: S
  • Ball: B
  • Foul ball: F
  • Hit by pitch: H
  • Ball in play: X

(For those wondering, this is a very very simplified version of Retrosheet's notation)


Your program must output 1 of 3 possible outputs to signify 1 of the mutually exclusive outcomes:

  • Strikeout
  • Walk
  • Ball in play

It doesn't matter what the outputs are exactly, as long as they are guaranteed to be distinct.


For those unfamiliar with the rules of baseball:

  • 3 strikes results in a strikeout
  • 4 balls results in a walk
  • A foul ball is a strike UNLESS the batter already has 2 strikes, in that case nothing happens
  • Hit by pitch immediately results in a walk
  • "Ball in play" immediately results in the "Ball in play" outcome

You may assume that:

  • the input string is encoded as ASCII
  • the input string represents an entire plate appearance (in other words it will end in one of the 3 outcomes above)
  • there are no other characters other than the ones above

You may not assume that:

  • there are no extra pitches/characters after the plate appearance is supposed to legally end
  • your program must return on the last pitch/character

Examples:

"Strikeout"fy:

SSS
SBSBS
BBBSSS
BSBSBS
BSFFBFS
BBSSFFFFFFFFS
SSSBBBB
FSS

"Walk"y:

BBBFB
BBBB
BBBSB
SSBBBB
SSH
SBSFFBFFFFBFFFFFFFFB
BBBBSSS
HSSS
SBBBBSS

"Ball in play"ly:

X
SSX
BBBX
SBSX
FFFFFFFFFFFFFFX
XBSBSBSB

This is , so fewest bytes wins.

(This challenge was inspired by this YouTube video)

\$\endgroup\$
  • 3
    \$\begingroup\$ How does XBSBSBSB work? The ball's in play, and then there's a strikeout, and then a fourth ball and it ends up in play? \$\endgroup\$ – Noodle9 Apr 17 at 20:48
  • 2
    \$\begingroup\$ @Noodle9 "You may not assume that there are no extra pitches/characters after the plate appearance is supposed to legally end." This is a dirty input case. Your program should return after parsing the X, effectively ignoring the rest of the characters. \$\endgroup\$ – dan9er Apr 17 at 20:52
  • 2
    \$\begingroup\$ oh, duh, I meant this one is closely related. \$\endgroup\$ – Giuseppe Apr 17 at 21:14
  • 2
    \$\begingroup\$ It seems everything about the ball in play is missing. I don't know baseball terminology so I have no idea what sort of state this even indicates. Could you explain this for people who don't know baseball terminology? \$\endgroup\$ – Ad Hoc Garf Hunter Apr 19 at 1:43
  • 2
    \$\begingroup\$ What does this mean: "You must not asumme that: ... your program must return on the last pitch/character" ? I can't find any sensible interpretation for this sentence? \$\endgroup\$ – Steve Bennett Apr 20 at 13:13

15 Answers 15

11
\$\begingroup\$

Retina 0.8.2, 22 bytes

4=`B
H
2`F|S

1!`[XHS]

Try it online! Link includes test cases. Outputs X for ball in play, H for walk, and S for strikeout. Explanation:

4=`B
H

The fourth ball results in a walk, same as hit by pitch.

2`F|S

The first two foul balls/strikes are ignored.

1!`[XHS]

Take the first available result.

| improve this answer | |
\$\endgroup\$
7
\$\begingroup\$

JavaScript (ES9),  68 66  55 bytes

Saved 11 bytes thanks to @Neil!

Returns \$S\$ for Strikeout, \$X\$ for Ball in play or an empty string for Walk.

s=>/(?<=(B.*){4})|(?=H)|X|(?<=([SF].*){2})S/.exec(s)[0]

Try it online!

How?

We match either:

  • (?<=(B.*){4}) : an empty string preceded by 4 B's
  • (?=H) : an empty string followed by an H
  • X : the character X
  • (?<=([SF].*){2})S : an S preceded by 2 other strike characters (S or F)

As a result, we get an S for a Strikeout, an X for a Ball in play, or an empty string for a Walk.

| improve this answer | |
\$\endgroup\$
  • 3
    \$\begingroup\$ (S|F) can be [SF], but if S, X and (empty string) are acceptable outputs, you can just use s=>/(?<=(B.*){4})|(?=H)|X|(?<=([SF].*){2})S/.exec(s)[0]. \$\endgroup\$ – Neil Apr 18 at 10:12
  • \$\begingroup\$ I have a "SyntaxError: invalid regexp group" (because non-fixed width lookbehind it seems) when I try in the console or in Regex101 (or with PHP, no I swear I wasn't about to port the answer).. How can it work for NodeJS? \$\endgroup\$ – Kaddath Apr 20 at 10:30
  • \$\begingroup\$ @Kaddath This should work fine in recent versions of Chrome, Edge or Node, but not yet in Firefox. \$\endgroup\$ – Arnauld Apr 20 at 10:57
  • \$\begingroup\$ And so should I conclude that ES9 supports non-fixed width lookbehinds (although not on all browsers) while PCRE doesn't? \$\endgroup\$ – Kaddath Apr 20 at 14:52
  • \$\begingroup\$ @Kaddath I think so, but I didn't check the ECAMScript spec. \$\endgroup\$ – Arnauld Apr 20 at 15:24
5
\$\begingroup\$

Python 3, 90 88 86 85 bytes

-2 bytes thanks to @ovs

s=b=0
for i in map(ord,input()):s+=~i%2*~s*i%71<3;b+=i<67;b//4+s//3==i%4//2>exit(i%6)

Try it online!

Returns via exit code: 0 for a walk, 4 for ball-in-play, and 5 for a strikeout.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ The last statement can be b//4+s//3>=(i%8<1)>exit(i%6) for -2. \$\endgroup\$ – ovs Apr 19 at 18:11
  • \$\begingroup\$ @ovs Thanks! I had to adjust it slightly to get it to work, but I used the same idea \$\endgroup\$ – math junkie Apr 19 at 19:14
4
\$\begingroup\$

05AB1E, 28 26 24 22 20 bytes

Ç.Δ6Ö½$¾4@N¾Ì@)yè}6%

Try it online!

Outputs 5 for strike out, 0 for walk, or 4 for ball in play.

Ç                   # convert the input to a list of ASCII codepoints
 .Δ              }  # find the first codepoint y such that:
   6Ö               #  is y divisible by 6? (true for B and H only)
     ½              #  if yes, increment the counter variable c
      $             #  push 1 and input
       ¾4@          #  is c >= 4?
          N¾Ì@      #  is the iteration count N >= c + 2?
              )     #  wrap the stack in a list: [1, input, c >= 4, N >= v + 2]
               yè   #  get the y-th element of that list (wraps around)

# * H and X are mapped to 1, so those characters always result in a match
# * nothing is mapped to `input`, it's just there to pad the list
# * B and F are mapped to `c >= 4`, so the 4th B results in a match (this would also
#   match HBBB or BBBBF, but we stop after the first match, so that's irrelevant)
# * S is mapped to `N >= c + 2`, so an S matches if it's preceded by at least 2 characters not in (B, H)

6%                  # after the loop: modulo 6 (B => 0, H => 0, S => 5, X => 4)
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Very nice! Got a lot of small things that makes the total great. :) Like the $ to push a 1 for both X and H, as well as a filler value (the input) that won't be indexed. Or the 6Ö½ to only increase the counter variable for H and B. \$\endgroup\$ – Kevin Cruijssen Apr 22 at 10:30
4
\$\begingroup\$

SNOBOL4 (CSNOBOL4), 172 bytes

	I =INPUT
N	I LEN(1) . P REM . I	:($P)
S	S =LT(S,2) S + 1	:F(K)S(N)
B	B =LT(B,3) B + 1	:F(H)S(N)
F	S =LT(S,2) S + 1	:(N)
X	OUTPUT =0	:(E)
K	OUTPUT =1	:(E)
H	OUTPUT =2
E
END

Try it online!

0 for a ball in play, 1 for a strikeout, and 2 for a walk.

gosh, I miss baseball :-(

Thanks to Mitchell Spector for pointing out several bugs!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I like seeing a SNOBOL answer, but this seems to be incorrect for SSBBBBS -- it says it's a strike-out, but it should be a walk. tio.run/##VY0xC8IwEIXny6@4wSGHRUx1KmQJXiG0xuCltLOz2MH/… \$\endgroup\$ – Mitchell Spector Apr 18 at 18:45
  • \$\begingroup\$ @MitchellSpector I think the LE(B,3) should be LT(B,3). I've always had trouble with off-by-one issues. \$\endgroup\$ – Giuseppe Apr 18 at 23:09
  • \$\begingroup\$ I see what you're doing now -- it looks good. The only issue remaining is not handling H (batter hit by pitch), but that's easily taken care of! \$\endgroup\$ – Mitchell Spector Apr 19 at 19:13
  • 1
    \$\begingroup\$ @MitchellSpector thanks, sorry, it's been a busy few weeks. \$\endgroup\$ – Giuseppe May 18 at 21:04
3
\$\begingroup\$

Retina 0.8.2, 42 41 bytes

Thanks to @Neil for saving a byte!

(?<=(B.*){3})B
H
1!`X|H|(?<=([SF].*){2})S

Try it online!

Based on @Arnauld's JavaScript answer.

Returns X for ball-in-play, H for a walk, and S for a strikeout.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ (S|F) can be [SF]. \$\endgroup\$ – Neil Apr 18 at 10:13
3
\$\begingroup\$

C (gcc), 124 \$\cdots\$ 87 86 bytes

Saved 12 13 bytes thanks to ceilingcat!!!
Saved 6 bytes thanks to dingledooper!!!

c;b;s;f(char*p){for(b=s=0;c=*p++-66,s+=c==4&s<2|c==17,b+=!c,c!=6&b<4&s<3&c<22;);c%=3;}

Try it online!

Returns \$0\$ for a walk, \$1\$ for a ball in play, and \$2\$ for a strikeout.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Io, 142 bytes

Returns 0 for a strikeout, 5 for a walk, 4 for a ball-in-play.

method(I, s :=b :=0
I foreach(i,s=s+i%2+if((s-1)*i%69<3,1,0);b=b+if(i<67,1,0);if(((b/4)floor!=0)or(i%8<1)or((s/3)floor!=0),System exit(i%6))))

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 121 bytes: Try it online! (Note that strikeout returns 5, walk returns 0 for this one) \$\endgroup\$ – math junkie Apr 19 at 16:07
2
\$\begingroup\$

05AB1E, 32 bytes

ηε.•Çšy•uS¢`+3@y'SÅ¿*·s4@r;M}0Kн

Outputs 2 for Strikeout; 1 for Walk; and 0.5 for Ball in play.

Try it online or verify all test cases.

Could be 31 bytes by removing the u if we can take the input as lowercase.

Explanation:

η              # Get all prefixed of the (implicit) input-string
 ε             # Map each prefix to:
  .•Çšy•       #  Push compressed string "xhbsf"
        u      #  Convert it to uppercase: "XHBSF"
         S     #  Convert it to a list of characters: ["X","H","B","S","F"]
          ¢    #  Count each character in the current prefix-string
  `            #  Push the counts separated to the stack
   +           #  Add the counts of "S" and "F" together
    3@         #  Check that it's >= 3
           *   #  And:
      y'SÅ¿   '#  Check whether the current prefix ends with an "S"
            ·  #  And double this combined check (so 2 if truthy; 0 if falsey)
   s           #  Swap so the count of 'B' is at the top of the stack
    4@         #  Check that it's >= 4 (1 if truthy; 0 if falsey)
   r           #  Reverse the stack, so the count of 'X' is at the top of the stack
    ;          #  Halve it (0.5 if truthy; 0.0 if falsey)
  M            #  Push the largest value of the stack
 }0K           # After the map: remove all 0s
    н          # And pop and push the first value of the list
               # (after which it is output implicitly as result)

Note that this assumes the count of "H" and "X" can never be larger than 1, which we can due to the assumptions mentioned in the challenge description.

See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•Çšy• is "xhbsf".

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

[C#], 147 bytes

d=(p,s,b,i)=>{var P=p[i];int t=0;if(P=='B')b++;if(P=='S'||(P=='F'&&s<2))s++;if(P=='H'||b>3)t=2;if(P=='X')t=3;if(s>2)t=1;return t!=0?t:d(p,s,b,++i);};

Try It online!

Return values: 1 = Strikeout, 2 = Walk, 3 = Ball in play.

(I wanted to use recursion for this answer, for learning)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Hi, welcome to CGCC! If you're using a recursive method, the d= should be counted towards the byte-count as well, so this is actually 149 bytes. But, you can golf this to 122 bytes by nesting ternary-ifs; changing the characters to code-point integers for the checks; and changing all || and && to | and & respectively (except for the || in P=='S'||(P=='F'&&s<2), since we need it to get rid of the parenthesis). \$\endgroup\$ – Kevin Cruijssen Apr 20 at 9:43
  • \$\begingroup\$ Also, if you haven't seen it yet, tips for golfing in C# and tips for golfing in <all languages> might both be interesting to read through. Enjoy your stay! :) \$\endgroup\$ – Kevin Cruijssen Apr 20 at 9:43
  • \$\begingroup\$ Thanks Kevin, I just saw this, I'll check it out, and improve my answer! \$\endgroup\$ – Kale_Surfer_Dude Apr 28 at 0:02
1
\$\begingroup\$

Z80Golf, 49 bytes

00000000: 0603 0e04 cd03 80fe 5320 0105 fe42 2001  ........S ...B .
00000010: 0dfe 4620 0310 0104 fe48 2001 4afe 5828  ..F .....H .J.X(
00000020: 0caf b93e 5728 06af b820 d93e 53cd 0080  ...>W(... .>S...
00000030: 76                                       v

Try it online!

Prints W for walk, S for strikeout, and X for ball in play.

Ungolfed

        ld b,3  ; number of strikes until strikeout
        ld c,4  ; number of balls until walk
input:
        call $8003      ; A = next character from stdin
strike:
        ; if A == 'S', record a strike
        cp 'S'
        jr nz,ball
        dec b
ball:
        ; if A == 'B', record a ball
        cp 'B'
        jr nz, foul
        dec c
foul:
        ; if A == 'F',
        cp 'F'
        jr nz, hit_by_pitch
        ; record a strike
        ; jump to the next section if batter hasn't struck out
        djnz hit_by_pitch
        ; if batter has struck out, give them another chance
        ; foul balls can't strike out a batter
        inc b
hit_by_pitch:
        cp 'H'
        jr nz, ball_in_play
        ld c, d ; d is initialized to 0 by default
ball_in_play:
        cp 'X'
        jr z, exit
walk:
        xor a   ; 1 byte shorter than ld a, 0
        cp c
        ld a, 'W'
        jr z, exit
strikeout:
        xor a
        cp b
        jr nz, input
        ld a, 'S'
exit:
        call $8000
        halt
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

sed -E, 69 60 59 bytes

s/([FS][^FS]*){2}S/&K/;s/([^B]*B){4}/&H/;s/[^KHX]*(.).*/\1/

Try it online!

9 bytes off, thanks to math junkie.

Input on stdin.

Output on stdout: H for walk, K for strikeout, X for in play.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I think you can save some bytes by returning H and X instead of w and p. I don't know sed too well though, so I could be wrong \$\endgroup\$ – math junkie Apr 19 at 15:25
  • \$\begingroup\$ @mathjunkie Good idea -- thanks! \$\endgroup\$ – Mitchell Spector Apr 19 at 15:45
0
\$\begingroup\$

Charcoal, 52 bytes

≔⪪S¹θF⪪B34HF02SS02F⁴F✂⌕Aθ§ι⁰I§ι¹I§ι²§≔θκ§ι³§Φθ№XHSι⁰

Try it online! Link is to verbose version of code. Outputs X, H or S as appropriate. Explanation:

≔⪪S¹θ

Split the input string into an array of characters. (If that is a legal input format, then these 5 bytes could be removed, but it would be really hard to enter in any of the examples.)

F⪪B34HF02SS02F⁴

Iterate over a string literal which encodes the following rules: The fourth Ball counts as a Hit by pitch. The first two Fouls might count as Strikes. The first two Fouls or Strikes don't strike the batsman out so turn them back into Fouls.

F✂⌕Aθ§ι⁰I§ι¹I§ι²

Find the range of matches of the source character that we're interested in.

§≔θκ

Update those matches with the destination character.

§ι³§Φθ№XHSι⁰

Filter out all characters except X, H and S and output the first remaining character.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Javascript, 84 characters

z=>{y={B:-1,S:i=0,F:0};while(c=z[i++],d={H:'B',F:'S'}[c]||c,y[d]++,y[c]<3);return d}

Returns S, X or B for a strike-out, on-base or walk.

Auto-formatted version:

f = (z) => {
    y = { B: -1, S: (i = 0), F: 0 };
    while (((c = z[i++]), (d = { H: 'B', F: 'S' }[c] || c), y[d]++, y[c] < 3));
    return d;
};
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Foxrabbit's Finite-State Binary (FSB), 57 bytes

The 57 bytes in hexadecimal are:

01
04 43 04 01 
06 47 08 02 
06 49 0c 03 
06 54 09 0c 
00 00 05 05 
02 04 06 07
01 00 00 00
02 48 07 0c
01 02 0a 0b
01 00 0a 0a
02 03 0b 0c
00 00 00 00
05 00 0d 0d
07 00 0d 0d

Assumes an ASCII-superset environment. Outputs a single byte with value 3 for strike-out, value 72 (ASCII 'H') for walk, or value 88 (ASCII 'X') for ball in play.

As an FFM program:

Get;inp;67;Ball:NotB
NotB;nop;71;Foul:NotBF
NotBF;nop;73;Print:NotBFH
NotBFH:nop;84;Strike:Print
Ball;lft;0;CountBall:CountBall
CountBall;inc;4;BallRet:SetH
BallRet;rgt;0;Get:Get
SetH;inc;72;SetH:Print
Foul;rgt;2;CountStrike:StrikeRet
Strike;rgt;0;CountStrike:CountStrike
CountStrike;inc;3;StrikeRet:Print
StrikeRet;lft;0;Get:Get
Print;out;0;End:End
End;hlt;0;End:End

The VM for both languages is a finite-state machine with access to an infinite tape of byte values. I used the initial tape cell for the most recent input character, the cell to the right for the number of strikes, and the cell to the left for the number of balls.

Try it online! In that link, the header and footer are the FFB interpreter written in Python and linked from the esolangs page, but modified a little to take an io.BytesIO instead of opening a binary file, and to skip the chr on an output instruction (since a printed byte 3 is annoying to verify). So the output there will be the decimal string "3", "72", or "88".

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.