text:
One line Keyboard
The Challenge
The goal of this challenge is to determine whether a given String can be typed using only one line of a standard UK QWERTY keyboard.
This is code golf, so shortest solution in bytes wins!

IO
Input will be a single String of zero or more characters in the ASCII decimal range of 32-126 inclusive.
You may assume for this challenge that an empty String requires no typing and thus can be typed using a single line.
You may take the input as a String, list of characters, or equivalent form for your language.
Output should be a truthy value for any String that can be typed using a single line, or falsey for one that cannot.

Keyboard layout
To clarify any ambiguity over what the standard keyboard layout is below is a list of keys available on each line, including alternate upper keys (accessed using shift).

Line 1

Standard:  `1234567890-=

Line 2

Standard:  qwertyuiop[]

Line 3

Standard:  asdfghjkl;'#
Uppercase: ASDFGHJKL
Special:   Caps Lock

Line 4

Standard:  \zxcvbnm,./
Alternate: |<>?
Uppercase: ZXCVBNM
Special:   Shift

Line 5

Special:   Space Bar

Alternate upper keys can only be pressed if Shift is also on the same line, and uppercase keys can only be accessed through Caps Lock or Shift. You really can only use one keyboard line!

Test cases
            -> true     (empty string)
45-2=43     -> true     (line 1)
qwerty      -> true     (line 2)
tryitout    -> true     (line 2)
Qwerty      -> false    (no shift or caps on line 2)
#sad        -> true     (line 3)
AsDf        -> true     (caps lock used)
@sDF        -> false    (no shift for alternate upper)
zxcvbn?     -> true     (line 4)
zxc vbn     -> false    (spacebar on separate line)
123abc      -> false    (multiple lines)
            -> true     (just space bar)
!!!         -> false    (exclamation marks cannot be printed by a single line)

So this is strictly U.S. keyboard layouts? (I've got a UK layout).

@ouflak On the contrary, it's strictly only the UK QWERTY keyboard layout

@Arnauld Yes I did, thanks for noticing!

Yeah I started looking at both and noticed your layout seems to follow the my UK keyboard layout, not my U.S. one. Hmmm... Wonder what my Austrian one looks like in comparison.

Are we allowed to take the input as a list of characters, or does it have to be a string?

@KevinCruijssen Yes, input is flexible, I've added that to the question.

Python 2, 130 123 121 115 bytes
lambda s:any(set(s)<=set(l+l.lower())for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!

Python 3, 111 bytes
lambda s:any({*s}<={*l+l.lower()}for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!
-4 bytes, thanks to nedla2004

If you are willing to use Python 3, you can get it down to 111 bytes.

Retina 0.8.2, 72 71 bytes
`^([-=\d`]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$

Try it online! Explanation: Each alternation matches a different row of the keyboard. The (?i) in the middle of the pattern causes the entire rest of the pattern to be matched case-insensitively. Edit: Saved 1 byte thanks to @KirillL.

opqr can be changed to o-r for -1. Also, did you forget a backtick character for Line 1, I don't see it?

@KirillL. Whoops, must have accidentally missed selecting it when copy/pasting, thanks for spotting that.

nice [][... :)

05AB1E, 66 47 bytes
žh…`-=«žS„[]«žTDu…;'#««žUDu"\,./|<>?"««ð)εISåP}O

Takes the input as a list of characters.
-19 bytes thanks to @Emigna. Completely forgot we had qwerty-keyboard constant builtins. :D
Try it online or verify all test cases.
Explanation:
žh                # Push "0123456789"
  …`-=            # Push "`-="
      «           # Merge them together
žS                # Push "qwertyuiop"
  „[]             # Push "[]"
     «            # Merge them togeter
žT                # Push "asdfghjkl"
  Du              # Duplicate, and toUppercase the copy
    …;'#          # Push ";'#"
        ««        # Merge all three together
žU                # Push "zxcvbnm"
  Du              # Duplicate, and toUppercase the copy
    "\,./|<>?"    # Push "\,./|<>?"
              ««  # Merge all three together
ð                 # Push a space
 )                # Wrap all string in an array
  ε   }           # Map each to:
   I              #  Take the input (list of characters)
    å             #  Check for each if it's in the current string of the map-iteration
     P            #  Take the product (1 if all are truthy, 0 otherwise)
       O          # Take the sum (either 1 or 0, so truthy/falsey), and output implicitly

Perl 5 -pl, 76 bytes
$_=/^( *|[\d`=-]+|[][wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/

Try it online!
The obvious regex approach.

Ruby -n, 86  81 bytes
p /^( *|[\d`=-]+|[\]\[wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/?1:0

Try it online!

JavaScript (Node.js), 99 98 95 bytes
x=>/^([-`=\d]+|[wetyuio-r[\]]+)$/.test(x)|/^([asdfghjkl;'#]+|[zxcvbnm,./<>?\\|]+| *)$/i.test(x)

Try it online!
-1 from the comment by @Kirill L. in the Retina answer.
-3 thanks @Ismael Miguel and @Arnauld for their combined effort.

Why do you have asdfghjkl and ASDFGHJKL in there? Why don't you use the i flag, like x=>/.../i.test(x)?

@IsmaelMiguel because QWERTYUIOP shouldn't be matched by the regex. The extra check will cost more than coding those uppercase letters directly into the regex.

Nevermind, it is buggy. x=>/^([-`=\d]+|[wetyuio-r\[\]]+)$/.test(x)||/^([asdfghjkl;'#]+|[zxcvbnm,.\/<>?\\|]+| *)$/i.test(x) is the same length

@IsmaelMiguel Using a bitwise | saves 1 byte.

@IsmaelMiguel the brackets after ^ and before $ are still needed. Turning || to | does save one tho.

You're right! 0|1 or 1|0 is still 1 which is a truthy value

Do you really need to escape [ and /?

The first [ can be unescaped. That is, the expression is now /^([-`=\d]+|[wetyuio-r[\]]+)$/. The / in the 2nd can be unescaped, making the expression be /^([asdfghjkl;'#]+|[zxcvbnm,./<>?\\|]+| *)$/i.

@Arnauld I thought I need to escape / everywhere in the regex lol. Thanks

I though too. Seems like that, inside a character class (is this the right name????) you don't need to escape those. At least Google Chrome was happy about it.

@IsmaelMiguel This is part of the ECMAScript specification. The only 3 characters that need to be escaped within a character class are \ , ] and - (see ClassAtomNoDash in the spec). The dash may also appear unescaped if it's either the first or the last character (otherwise, it's interpreted as a character range separator).

@Arnauld That is actually very interesting. Thank you! I already knew about the 2nd part of your comment, but the first part is totally new to me.

Perl 6, 102 101 100 bytes
-1 byte thanks to nwellnhof!
->\a{max map {a.comb⊆comb $_~.lc:},|<eqwrtyuiop[] ASDFGHJKL;'# ZXCVBNM\,./|<>?>,' ',"`-="~[~] ^10}

Try it online!
Pretty standard implementation. There's probably a shorter regex based solution to this, but I don't know Perl 6's regex well enough to make an attempt.

You can use max instead of ?any (and min instead of ?all).

For what it's worth, a regex-based solution would be 84 bytes or 80 bytes using Perl 5 regexes with m:P5//. But I don't think either of them are worth posting as answer.

Java 10, 209 208 bytes
s->{int l=0,t;for(var p:s){t=p.matches("[[0-9]`\\-=]")?1:"qwertyuiop[]".contains(p)?2:p.matches("(?i)[asdfghjkl;'#]")?3:"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?4:p.equals(" ")?5:9;l=l<1?t:l!=t?9:l;}return l<6;}

-1 byte thanks to @TFeld.
Try it online.
Explanation:
s->{                    // Method with String-array parameter and boolean return-type
  int l=0,              //  Line-integer, starting at 0
      t;                //  Temp integer
  for(var p:s){         //  Loop over the characters
    t=p.matches("[[0-9]`\\-=]")?
                        //   If it's a character from the first line:
       1                //    Set `t` to 1
      :"qwertyuiop[]".contains(p)?
                        //   Else-if it's a character from the second line:
       2                //    Set `t` to 2
      :p.matches("(?i)[asdfghjkl;'#]")?
                        //   Else-if it's a character from the third line
       3                //    Set `t` to 3
      :"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?
                        //   Else-if it's a character from the fourth line:
       4                //    Set `t` to 4
      :p.equals(" ")?   //   Else-if it's a space from the fifth line:
       5                //    Set `t` to 5
      :                 //   Else (invalid character):
       9;               //    Set `t` to 9
    l=l<1?              //   If `l` is still 0:
       t                //    Set it to `t`
      :l!=t?            //   Else-if `t` is a different line than `l`:
       9                //    Set `l` to 9 (non-existing line)
      :                 //   Else (`t` is the same line as `l`):
       l;}              //    Leave `l` the same
  return l<6;}          //  Return whether `l` is not 9

Nice solution, but it returns true for !!! despite exclamation marks being unable to be printed by any single line. I've just added that as a test case

@LukeStevens Ah, I falsely assumed the input is always valid for one of the five lines. I will modify my solution. It's a simple fix (but will add some bytes..)

Why not use (?i) for the other uppercase letters?

@Neil Because of the escape required for the slash, it's also 209 bytes.

Suggest p==" " instead of p.equals(" ")

Powershell, 87 bytes
Port of Neil's Retina regex.
"$args"-cmatch"^([-=\d``]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$"

Jelly, 55 bytes
ØDW;Øq;Œu$€3,4¦;"“rɠ3“Ż²“¿µ|“aƲƘ0ÞḅzḂ»⁶ṭ
¢f€ẈṠSỊafƑ¢F¤$

Try it online!
The first line yields a list of the keyboard lines and the second one checks whether the program input is contained on a single (or zero) lines and that there are no characters that can't be typed (like QWE!@#).

C, 150 bytes
x;f(char*s){for(x=0;*s;x|="@               "[*s++-32]);return!(x&x-1);}

Try it online!
It won't win any prizes, but this is a fun approach: we map characters on the five rows of the keyboard to 4 8 16 32 64, and every invalid character to 3. We bitwise OR the value for each character in the string together, and check if the result satisfies x&(x-1) == 0, which is true when x is a power of 2 or zero, i.e. whenever x has at most one bit set.

AWK, 163 119 113 bytes
This is an AWK answer, returns a numeric 1 string for true, 0 string for false.
(Written as AWK invoke as awk -f file for interactive use.)
{print/^[-`1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^ *$/}

Try it online!
However, does not handle TAB character as written (trivial extension) as not part of spec.

"All solutions to challenges should: (…) Be a serious contender for the winning criteria in use. For example, an entry to a code golf contest needs to be golfed, and an entry to a speed contest should make some attempt to be fast." – [help/on-topic] There is no need to declare those 2 variables and definitely no need to make them strings. And a single print is enough: Try it online!

Your comment that solutions should be serious is valid but I would argue that any solution therefore after the revised 05AB1E one is therefore irrelevant since they could not match or better the result.
I don't see how you can not use print twice to get both true and false (certainly for something that reads strings until end of input).  And I did say optimized not optimal at the time.  Thanks to your thoughts it can be reduced to 143 characters.

And in fact going all the way will make it 121 characters ...

[tag:code-golf] is not a competition for shortest code overall, but shortest for your language. If you can improve your solution, then I'd suggest doing so

@Jo King, thank you for your clarification(s).  revised code would/could be:
{print/^[-```1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^  *$/}

And for those who realize I don't now the formatting conventions despite reading the help, I apologise for the triple back-ticks.  And the AWK answer reduces to (when corrected back-quote(s) used in the code) to 121 bytes.

Usually answers on this site includes a link to a online tester so others can easily test the answers. Besides, I counts 119 bytes.

If you want to include backticks in code in comments you can use more than 1 backtick to wrap them. "``abc`def``"--> abc`def For posts just 4-space indent.

After further optimization reduces to:
{print/^[-`0-9=]$/||/^[]qwertyuiop[]$/||/^[asdfghjkl;'#ASDFGHJKL]$/||/^[zxcvbnm,./|<>?ZXCVBNM]$/||/^  *$/}
Try it online!

LUA, 282 262 259 270 bytes
s=io.read()l=0
t=0
for c in s:gmatch"."do
f=c.find
t=f(c,"[0-9%`-=]")or 0|(f(c,"[qwertyuiop%[%]]")or 0)*2|(f(c,"[aAsSdDfFgGhHjJkKlL:'@#~]")or 0)*4|(f(c,"[\\zxcvbnm,./|<>?ZXCVBNM]")or 0)*8|(f(c," ")or 0)*16
t=t==0 and 17or t
l=l<1 and t or l~=t and 17or l
end
print(l<17)

Try it online!

PHP, 98 bytes
I´m a little sad that there´s nothing shorter than regex. It´s probably not the fastest solution.
<?=preg_match("%^([`\d=-]*|[wetyuio-r[\]]*|(?i)[asdfghjkl;'#]*|[\\\zxcvbnm,./|<>?]*| *)$%",$argn);

Run as pipe with -F or try it online.

Shortest non-regex solution I found (124 bytes; linebreak and tab for reading convenience):
foreach(["`1234567890-=","qwertyuiop[]","asdfghjkl;'#ASDFGHJKL","zxcvbnm,./\|<>?ZXCVBNM"," "]as$d)
    trim($argn,$d)>""||die(1);

exits with code 1 for truthy, 0 for falsy. Run as pipe with -R.
Requires PHP 5.4 or later; for older PHP, use array(...) instead of [...] (+5 bytes)
or use these 123 bytes with -nR in PHP <7:
foreach(split(_,"`1234567890-=_qwertyuiop[]_asdfghjkl;'#ASDFGHJKL_zxcvbnm,./\|<>?ZXCVBNM_ ")as$d)
    trim($argn,$d)>""||die(1);

Bash, 119 bytes
Includes an echo to provide "readable" output.  If you put a suitable wrapper of shell (your choice) around it to include print/output, then you can save 8 bytes. My reading of the challenge suggests the solution should output a suitable output indication so I stick with 119 bytes.

[[ "$@" =~ ^[asdfghjklASDFGHJKL\;\'#]*$|^[-\`0-9=]+$|^[]qwertyuiop\[]*$|^[zxcvbnm,./\|\<\>\?ZXCVBNM]*$|^\ *$ ]];echo $?

Try it online!

Be advised the Bash solution also means my/@manatwork AWK solution may be reduced to 113 bytes.


text:
Send the pairs in smallest output
Write two programs A and B.

Program A (the encoder) takes 1024 pairs of integers \$(k,v)\$, where \$0≤k<2^{32}\$ and \$0≤v<1024\$, and all \$k\$ will be different. It will output a single positive integer (likely a few thousand digits long).
Program B (the decoder) takes two inputs: the output-integer of program A, and one \$k\$ from the input pairs we've used as input for program A. It will output the \$v\$ corresponding with the given \$k\$.

In other words, Program A compresses 1024 key-value pairs to the smallest size possible, and Program B must use the data provided by A to recover each value given its corresponding key.
Your score is the average value of the output of program A for the test data. Hardcoding for a given test case is forbidden, so this should be essentially the same as the score on the pretest data. Pretest data and test data are drawn from the same (uniform) distribution.
Random pretest data is available here.
Random.txt
MD5: 6FA62A283282F5CFBC6E089B2369C9B9
SHA1: 0BF7FB62A8566575D4A948DD34A559BAAAC422FC
CRC32: 2675DAB3

Testdata.txt
MD5: 6C8F90F126C8F3C55A9FC9F22733CE55
SHA1: F7D11AAB54393DE8CC4E75565950D8C6D9CFE42A
CRC32: B3DB1479

Actual test cases:
https://www.mediafire.com/folder/i6hmn9zzk73ig/174035
A stupid program A that work but totally didn't optimize is here. Its program B is easily written.

Comments are not for extended discussion; this conversation has been moved to chat.

Python 3, pretest ≈ 3.198 · 103085
from hashlib import sha512
import sys

def encode(pairs):  # program A
    elim = [[] for i in range(10)]
    done = set()
    code = 0
    i = 0
    while len(done) < 10 * len(pairs):
        e = 1 << i
        for a, e1 in elim[i % 10]:
            if decode(e, a) & 1 << i % 10:
                e ^= e1
        for a, b in pairs:
            if (a, i % 10) not in done and decode(e, a) & 1 << i % 10:
                elim[i % 10].append((a, e))
                done.add((a, i % 10))
                if (decode(code, a) ^ b) & 1 << i % 10:
                    code ^= e
                print("progress:", len(done), "of", 10 * len(pairs))
                break
        i += 1
    return code

def decode(code, a):  # program B
    k = -(-code.bit_length() // 8)
    a_bytes = a.to_bytes(4, "little")
    s = b"".join(
        sha512(a_bytes + i.to_bytes(4, "little")).digest() for i in range(-(-k // 64))
    )
    b = 0
    for i, (x, y) in enumerate(zip(code.to_bytes(k, "little"), s)):
        b ^= (x & y) << i * 8 % 10
    return b & 1023 ^ b >> 10

def read_pairs():
    return [tuple(map(int, line.split())) for line in sys.stdin]

def test(pairs):
    code = encode(pairs)
    for a, b in pairs:
        assert decode(code, a) == b
    print(code)

test(read_pairs())

Pretest output
The encoder (program A) takes about 6 minutes to run in PyPy.  Its output for the pretest is:
31983609135966228809777741024866634396367955850954563312910243722966419655757435319862170247982609333805811315728884450895580799383047152743378820273077053884539894357668608677003723034054150072472679319590110643492810118920515801395958239291576592748237372467748241886558720871264342908155191777359585845696600370935487361341785211913110481040589139281898484188938846817560295768498881287225642023856190028544427590648046456036574330957328066402924125404073006144179946215625958643494081212962075340324684130280414625483652805036566339457267551141894682726365505972133784971174734160698976154775811488656096461968463830423074194023690503590590775033695214423998615444187716064686144001451215863398975347419967640878071667106313050954800893007781291647431510389526738768573353015452189679155051541561231146940706328809607485776138460159591319080343284358448724370020894125934538601889550086934233091980254236012141359542061770911092807766526451019824031548581065916355093797436687555279312288875998494044065641530299531661256941700326425163573657004343895103496724901715610405315696830871086538183435694121760474073023458471744226820280782007502797691083190318324487197753443963571848733746071847847503077912802000091614327968964141450181973360651625471354587026290724846906554164924720066533282988840115094797514460217879380690461261835733295861803705394151420199059533051945944829244868440580371672125590557347324783817602474773801825029895884776404639198926793779867410515553784121435319134307253638568331529787781235084628432871984360992875472633958491882569077293145250495277979952525973740358428525766027507530317692602517027217679810063987302625422496799748842451355136627998296037177558122844929449377522984595368107136303866235523533278732324181522202203636626264487517560120680389621562028772569157498411869721766672498939022664474767985892820170334179252554511620260319259569593256276255895000364202791049913505972232782566530566341223654980901733905002516092163056581626593518077383131036496001950185439305868415290006860241666136700655660075381115903744293277973930796274779653647714016073922883203769217980038066409200998466193202329090278491648479911508276027286548829602558975386937067679077276971208214625876007703346632539055994199462748282900998214486645162277623073498410220953186861172886747394992230973314265902007187446129816157393697305312624793190210450979525381906198852578619071224265649145441460469662250448218764499047348362548750003370944772956154662554023857705149861774126960045122299555516709305181620013892453158471100545618056605123335183465355868672237737792628988379889459029444873409805140995088383991356246685699313569301017907549835057806089472106637383136584875471496123974387658318450782138871582131161643221907721116143710024435066884630344739171885220542678102635637192667329306874983507141585026570891055701250722003923329858567937864129882737742073320064153568198296932909448928951903564049064190959836027425436414766746048788435990514173396582362591284614310722316033029018983309186148295917238363165751109139219360789557442877663768774219

which you can use to verify the much faster decoder (program B) if you don’t want to wait for the encoder.
How it works
The decoder expands \$a\$ into a deterministic pseudorandom stream using SHA-512, bitwise ANDs the stream with the code (output of program A), and bitwise XORs all the base 1024 digits of the result.
It has the important property that the decoder is a linear function of the code: decode(code1 ^ code2, a) == decode(code1, a) ^ decode(code2, a).  Therefore, the encoder can compute the needed code using Gaussian elimination.
Scoring note
For official scoring, please make the thoroughly negligible “output formatting” adjustment of adding 1 to the code value, since otherwise a theoretically possible output is 0, which is not a positive integer.  As of this writing, the other answer has the same property.

Wolfram Language (Mathematica), \$n \approx 2.088\times 10^{5033}\$ for pretest
a=Block[{aValues,bValues,p,poly,i},
  {aValues,bValues}=Thread@#;
  p=NextPrime[nPair-1];i=0;
  While[!DuplicateFreeQ[aValues~Mod~p],
    p=NextPrime@p;++i;
  ];
  coef=CoefficientList[PolynomialMod[
    InterpolatingPolynomial[Thread@{aValues,bValues},x,
      Modulus->p],p],x];
  coefval=FromDigits[Reverse@coef,p];
  FromDigits[
    Join[{coefval},8191~Table~Floor[i/8191],{i~Mod~8191}]
    ,8192]+1
]&;
b=Block[{i,p,coef,n,aValue},
  n=#-1;i=0;
  While[
    {n,p}=QuotientRemainder[n,8192];
    i+=p;
    p==8191];
  p=NextPrime[nPair-1,i+1];
  coefReversed=IntegerDigits[n,p];
  aValueQuery=#2;
  Fold[
    Mod[#*aValueQuery+#2,p]&,
    Table[0,Length@aValueQuery],
    coefReversed
  ]
]&;

Try it online! (generate random test case based on random seed)
Try it online! (pretest)
Use InterpolatingPolynomial.
Due to the Birthday's paradox, this is not as efficient as I expected.

As Anders says, this code doesn't work for {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},...,{1022,0},{1023,0}}. Either add 1 to the result or map zero to an impossibly reached value

I guess You should give your pretest score, not the code size: Smallest average value of the output of program A over a fixed collection of random test cases wins

@titus fixed. ''

@l4m2 fixed. '''

Python 3, 1.873x104405
import sys
inputs = sys.stdin.read()

numbers = [list(map(int, l.split(' '))) for l in inputs.strip().split('\n')]
def encode(numbers):
  output = ''
  def allNumbers(numbers, a, b):
    nonlocal output
    if len(numbers) == 0: output += '00'
    elif len(numbers) == 1:
      output += '0'
      output += bin(1024 + numbers[0][1])[2:]
    else:
      output += '1'
      l, r = [], []
      m = (a + b) // 2
      for item in numbers:
        if item[0] < m: l.append(item)
        else: r.append(item)
      allNumbers(l, a, m)
      allNumbers(r, m, b)
  allNumbers(numbers, 0, 2 ** 32)
  return int(output, 2)

def decode(output, key):
  output = bin(output)[2:]
  index = 0
  def peak(n):
    return output[index:index + n]
  def skip(n):
    nonlocal index
    index += n
  result = 0
  def findRange(a, b):
    nonlocal result
    if peak(1) == '0':
      if peak(2) == '00':
        skip(2)
        return
      else:
        skip(2)
        n = int(peak(10), 2)
        if a < key < b: result = n
        skip(10)
        return
    skip(1)
    m = (a + b) // 2
    findRange(a, m)
    findRange(m, b)
  findRange(0, 2 ** 32)
  return result

output = encode(numbers)
print("%s.%sE+%d" % (str(output)[0], str(output)[1:10], len(str(output)) - 1))
for (key, value) in numbers:
  assert(decode(output, key) == value)

Try it online!

Sample answer: Python 3, pretest ≈ 3.612 · 1012946
This is just the sample program A given in the problem, along with a corresponding program B, to help those having trouble understanding the problem.
Program A simply concatenates the (32 + 10)-bit binary representations of all the input pairs.  Program B iterates through these (32 + 10)-bit chunks until it finds the chunk whose first 32 bits match the given \$a\$, and outputs the corresponding \$b\$ from its last 10 bits.
Program A
x = 0
for i in range(1024):
  [a,b] = input().split()
  x = x * 4398046511104 + int(a) * 1024 + int(b)
print (x)

Try it online!
Program B
x, a = map(int, input().split())
while (x % 4398046511104) // 1024 != a:
  x //= 4398046511104
print(x % 1024)

Try it online!

Didn't OP already provide one like that?

@user202729 OP provided exactly this program A and said “its program B is easily written”. I don’t know why people seem to be having so much trouble understanding this challenge, but in case an example with an explicit program B would help them, I’ve provided one in this community wiki.

@user202729 Also, the score on this sample answer is useful context for understanding the scores on the real answers—it might seem surprising at first glance that this answer is significantly far from optimal.

(this is secondary discussion, but) Personally, I think that (assume that the English don't have problems) people who cannot figure out what to do with the precise mathematical defintion (still, it looks pretty intuitive to me already), are unlikely to post competitive answers even if they do understand the task; on the other hand. Adding a "# Why this challenge is interesting" section takes effort, and the answers are probably not better too. The only problem is the downvotes, but I hope the OP don't mind that.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
List all possible titles for the Anno games
In the Anno video game series there are 6 games with a 7th one announced for early 2019. Their titles always feature a year in a specific pattern:
Anno 1602, Anno 1503, Anno 1701, Anno 1404, Anno 2070, Anno 2205, Anno 1800

The digital sum is always 9.
The years are four digits long.
They contain at least one zero.

Within these constrains there exist 109 possible titles:
[1008,1017,1026,1035,1044,1053,1062,1071,1080,1107,1170,1206,1260,1305,1350,1404,1440,1503,1530,1602,1620,1701,1710,1800,2007,2016,2025,2034,2043,2052,2061,2070,2106,2160,2205,2250,2304,2340,2403,2430,2502,2520,2601,2610,2700,3006,3015,3024,3033,3042,3051,3060,3105,3150,3204,3240,3303,3330,3402,3420,3501,3510,3600,4005,4014,4023,4032,4041,4050,4104,4140,4203,4230,4302,4320,4401,4410,4500,5004,5013,5022,5031,5040,5103,5130,5202,5220,5301,5310,5400,6003,6012,6021,6030,6102,6120,6201,6210,6300,7002,7011,7020,7101,7110,7200,8001,8010,8100,9000]

Your objective is to list them all in any reasonable form in the fewest number of bytes.

How flexible is the output format? Is this acceptable?

@LuisMendo Yes, that's fine with me.

Are lists of digits allowed?

Are large numbers of spaces between number acceptable? 1008       1017     1035...

@aslum I assume you mean a lot of spaces, not just one, right? Comment markdown doesn't allow for a good representation of that. And I would assume that's allowed, given that Luis's format above is allowed. ;-)

Yeah, currently my code puts a space for every non-9-sum number... so 8 between the first and second result... 11 between third and fourth, and 899 spaces between last two... but as long as output goes to a standard web browser all duplicate spaces will be ignored, much like the spaces where ignored in my comment above.

@EriktheOutgolfer I'd say no to lists of digits because they really do not look like years anymore.

@aslum Excessive spaces are fine.

Hmm I stopped around Anno 1404, my assumption was that the difference between the titles is always 99 but then it would have been 2097 and 2196...

R, 59 51 bytes
Outputs the valid numbers as the names of a list of 201's. Why 201? Because ASCII 0 is 48, and 4*48+9 is... yeah. Saved 6 bytes by aliasing ^ to Map and another 2 by using 1:9e3 as range.
"^"=Map;x=sum^utf8ToInt^grep(0,1:9e3,,,T);x[x==201]

Try it online!
Explanation
# Create list of sums of ASCII char values of numbers,
# with the original numbers as the names of the list
x <- Map(sum,
  # Create a list from the strings where each element is the string split
  # into ASCII char values
  Map(utf8ToInt,
      # Find all numbers between 1 and 9e3 that contain a zero
      # Return the matched values as a vector of strings (6th T arg)
      grep(pattern=0,x=1:9000,value=TRUE)
  )
)
# Pick out elements with value 201 (i.e. 4-digits that sum to 9)
# This implicitly only picks out elements with 4 digits, since 3-digit
# sums to 9 won't have this ASCII sum, letting us use the 1:9e3 range
x[x==201]

ah, grep, why do I never remember that it casts to character...

Perl 6, 35 33 bytes
-2 bytes thanks to Jo King
{grep {.ords.sum==201&&/0/},^1e4}

Try it online!

Python 2, 67 66 64 bytes
print[y for y in range(9001)if('0'in`y`)*sum(map(ord,`y`))==201]

Try it online!

Saved:

-1 byte, thanks to Luis felipe De jesus Munoz
-2 bytes, thanks to Kevin Cruijssen

64 bytes by using the ord sum == 201 trick from other answers.

Jelly, 11 bytes
9ȷṢ€æ.ẹ9ṫ19

Try it online!
How it works
9ȷṢ€æ.ẹ9ṫ19  Main link. No arguments.

9ȷ           Set the left argument and the return value to 9000.
  Ṣ€         Sort the digits of each integer in [1, ..., 9000].
    æ.       Perform the dot product of each digit list and the left argument,
             which gets promoted from 9000 to [9000].
             Overflowing digits get summed without multiplying, so we essentially
             map the digit list [a, b, c, d] to (9000a + b + c + d).
      ẹ9     Find all 1-based indices of 9.
             Note that 9000a + b + c + d == 9 iff a == 0 and b + c + d == 9.
        ṫ19  Tail 19; discard the first 18 indices.

PowerShell, 50 49 bytes
999..1e4-match0|?{([char[]]"$_"-join'+'|iex)-eq9}

Try it online!
Constructs a range from 999 to 10000, then uses inline -match as a filter to pull out those entries that regex match against 0. This leaves us with 1000, 1001, 1002, etc. We then pipe that into a Where-Object clause where we take the current number as a string "$_", cast it as a char-array, -join those characters together with + and Invoke-Expression (similar to eval) to come up with their digit sum. We check whether that is -equal to 9, and if so it's passed on the pipeline. At program completion, those numbers are picked up from the pipeline and implicitly output.

JavaScript (ES6), 78 73 bytes
Saved 2 bytes thanks to @KevinCruijssen
Returns a space-separated string.
f=(n=9e3)=>n>999?f(n-9)+(eval([...n+''].join`+`)&/0/.test(n)?n+' ':''):''

Try it online!
How?
We iterate over the range \$[1008..9000]\$ with an increment of \$9\$, ignoring numbers that don't have a \$0\$.
All these numbers are multiples of \$9\$, so the sum of their digits is guaranteed to be a multiple of \$9\$ as well.
Because valid numbers have at least one \$0\$, they have no more than two \$9\$'s, which means that the sum of the remaining digits is at most \$18\$. Therefore, it's enough to test if the sum of the digits is odd.
Hence the test:
(eval([...n + ''].join`+`) & /0/.test(n)

You can save a byte changing the 1008 to 999, since it doesn't contain a 0 anyway, and 999+9 = 1008.

Or even 2 bytes by changing it to f=(n=9e3)=>n<1e3?'':(eval([...n+''].join`+`)<10&/0/.test(n)?[n,,]:'')+f(n-9) (does contain a trailing comma though, so f=(n=9e3)=>n<1e3?'':(eval([...n+''].join`+`)<10&/0/.test(n)?n+' ':'')+f(n-9) with space delimiter including trailing space might look prettier)

@KevinCruijssen Thanks! I'm actually trying to update this for a while, but I have like 500B/s of Internet bandwidth where I am tonight. :/

I know the feeling.. Lately our internet at home is crap for some reason.. Can't download anything above 10 MB, and sometimes have to refresh videos or pages with 10+ images a few times before it completely loads.. Really annoying when I'm working from home on Mondays/Tuesdays.. >.> Tomorrow someone comes to fix it (and I'm not leaving him until it's fixed xD)

JavaScript (Node.js), 89 bytes
[...Array(9e3)].map(_=>i++,i=1e3).filter(a=>(s=[...a+""]).sort()[0]<1&eval(s.join`+`)==9)

Try it online!

-4 bytes thanks to @ETHproductions

JavaScript (Node.js), 129 127 126 124 115 114 111 110 105 97 93 92 90 bytes
[...Array(9e3)].map(f=(_,i)=>eval(s=[...(i+=1e3)+""].sort().join`+`)-9|s[0]?0:i).filter(f)

Try it online!
Explanation
[...Array(9e3)].map(f=(_,i)=>eval(s=[...(i+=1e3)+""].sort().join`+`)-9|s[0]?0:i).filter(f)
[...Array(9e3)].map(f=(_,i)=>                                                  )           // Create a 9000-length array and loop over it; store the loop body
                                    [...(i+=1e3)+""]                                       // Add 1000 to the index and split it into an array of characters (17 -> ["1", "0", "1", "7"])
                                                    .sort()                                // Sort the array of characters in ascending order by their code points ("0" will always be first) (["1", "0", "1", "7"] -> ["0", "1", "1", "7"])
                                  s=                       .join`+`                        // Join them together with "+" as the separator (["0", "1", "1", "7"] -> "0+0+2+9"); store the result
                             eval(                                 )-9                     // Evaluate and test if it's different than 9
                                                                       s[0]                // Take the first character of the string and implicitly test if it's different than "0"
                                                                      |    ?0              // If either of those tests succeeded, then the number doesn't meet challenge criteria - return a falsey value
                                                                             :i            // Otherwise, return the index
                                                                                .filter(f) // Filter out falsey values by reusing the loop body

First time doing code golf in JavaScript. I don't think I need to say it, but if I'm doing something wrong, please notify me in the comments below.

-3 bytes thanks to @Luis felipe De jesus Munoz
-6 bytes thanks to @Kevin Cruijssen

[...Array(9e3)] instead Array(9e3).fill() saves 2 bytes

.map(a=>+a) instead .map(Number) saves another byte

You can remove the space at (_, i) to save a byte, and s[0]+s[1]+s[2]+s[3] can be eval(s.join`+`) to save an additional 4 bytes.

@KevinCruijssen Whoops! No clue how I missed that space there. Also, the eval trick is nuts, thanks a bunch for that one.

Also, I'm pretty sure the || can be | in your answer.

If you use .map() only to generate the range, and keep the filtering separate, you can save 8 bytes: Try it online!

83 bytes. Although you should either add _=> to the beginning to make it a function or make this a REPL solution.

87 bytes (n=999)=>[...Array(n)].map(_=>n+=9).filter(_=>/0/.test(_)&eval([..._+''].join\+`)==9)`

Python 2, 57 bytes
n=999
exec"n+=9\nif'0'in`n`>int(`n`,11)%10>8:print n\n"*n

Try it online!
2 bytes thanks to Dennis
Uses an exec loop to counts up n in steps of 9 as 1008, 1017, ..., 9981, 9990, printing those that meet the condition.
Only multiples of 9 can have digit sum 9, but multiples of 9 in this range can also have digits sum of 18 and 27. We rule these out with the condition int(`n`,11)%10>8. Interpreting n in base 11, its digit sum is equal to the number modulo 10, just like in base 10 a number equals its digit sum modulo 9. The digits sum of (9, 18, 27) correspond to (9, 8, 7) modulo 10, so taking those>8 works to filter out nines.
The number containing a zero is check with string membership. '0'in`n`. This condition is joined with the other one with a chained inequality, using that Python 2 treats strings as greater than numbers.

I like how heavily golfed Python seems to often have enormously long autogenerated executables...

sed and grep (and seq), 72 64 63 bytes
seq 9e3|sed s/\\B/+/g|bc|grep -wn 9|sed s/:9//|grep 0|grep ....

Some of these aren't four digits long (but I'm not sure what the final grep is, so maybe I'm running it wrong?)

@Sparhawk: The last grep ensures that the number is 4 digits long

@Thor Ah right. For some reason I parsed that as an ellipsis.

Haskell, 55 bytes
[i|i<-show<$>[1..5^6],201==sum(fromEnum<$>i),elem '0'i]

Thanks to @Laikoni, see the comments.
Readable:
import Data.Char (digitToInt)

[i | i <- show <$> [1000..9999]
   , sum (digitToInt <$> i) == 9
   , '0' `elem` i
   ]

Welcome to PPCG and Haskell golfing in particular! You can save a few bytes by dropping (-48+) and comparing the sum against 201 instead of 9. Incidentally this also allows you to use 1 instead of 1000 for the range.

Also your previous version without main=print was fine as per this consensus on Meta.

9999 can be 5^6 instead.

Ha, there's always another byte to shave! Thanks :-)

R, 82 bytes
write((x=t(expand.grid(1:9,0:9,0:9,0:9)))[,colSums(x)==9&!apply(x,2,all)],1,4,,"")

Try it online!
Generates a matrix x of all possible 4-digit numbers, excluding leading zeros, going down columns. Then filters for column (digital) sums of 9 and containing zero, i.e., not all are nonzero. write prints down the columns, so we write to stdout with a width of 4 and a separator of "".
Outgolfed by J.Doe

Nice answer! I came up with a different route...

Japt, 20 18 bytes.
-2 bytes thanks to @Shaggy and @ETHproductions
A³òL² f_=ì)x ¥9«Z×

A³òL² f_=ì)x ¥9«Z×  Full program
A³òL²               Range [1000, 10000]
      f_            Filter by :
        =ì)         Convert to array
           x ¥9     Sum equal to 9?
               «    And
                Z×  Product not 0

Try it online!

This is actually 28 bytes. Using a literal integer instead is 22 bytes but A³ò9000 f_ìx ¥9©ZsøT gets you back down to 20.

You can save 1 byte by using ì instead of s and ¬, which has to be done in the filter: f_=ì)x ¥9.... Then you can save another by checking if the product of Z is zero with «Z×: Try it online!

05AB1E, 15 13 12 10 bytes
₄4°ŸεW°ö9Q

-2 bytes thanks to @Emigna
-3 bytes thanks to @Grimy
Try it online.
Explanation:
₄4°Ÿ        # Create a list in the range [1000,10000]
    ʒ       # Filter this list by:
     W      #  Get the smallest digit in the number (without popping the number itself)
      °     #  Take 10 to the power this digit
       ö    #  Convert the number from this base to an integer (in base-10)
        9Q  #  Check if it's equal to 9

If the smallest digit is \$d=0\$ it will become \$1\$ with the \$10^d\$ (°). And the number in base-1 converted to an integer in base-10 (ö) would act like a sum of digits.
If the smallest digit is \$d=1\$ it will become \$10\$ with the \$10^d\$ (°). And the number in base-10 converted to an integer in base-10 (ö) will of course remain the same.
If the smallest digit is \$d=2\$ it will become \$100\$ with the \$10^d\$ (°). And the number in base-100 convert to an integer in base-10 (ö) would act like a join with 0 in this case (i.e. 2345 becomes 2030405).
If the smallest digit is \$d=3\$ it will become \$1000\$ with the \$10^d\$ (°). And the number in base-100 convert to an integer in base-10 (ö) would act like a join with 00 in this case (i.e. 3456 becomes 3004005006).
... etc. Smallest digits \$d=[4,9]\$ would act the same as \$d=2\$ and \$d=3\$ above, with \$d-1\$ amount of 0s in the 'join'.

If the smallest digit is \$>0\$ with the given range \$[1000,10000]\$, the resulting number after °ö would then be within the range \$[1111,9000000009000000009000000009]\$, so can never be equal to \$9\$. If the result is equal to \$9\$ (9Q) it would mean the smallest digit is \$d=0\$, resulting in a base-1 with °ö; and the sum of the digits was \$9\$.

₄4°Ÿʒ0å}ʒSO9Q. Splitting filters are usually shorter

@Emigna Ah, I was looking for a shorter way for the range, but completely forgot about 4°. Thanks. And you're indeed right that multiple loose filters (at the end) are shorter. Will also add it to one of my tip answers. Thanks for both bytes!

A strictly hypothetical solution is ∞4úʒ0å}ʒSO9Q (12 bytes), but I don't think it should count as valid so I'll leave it here as-is.

And my other 13-byter (inspired by the ord sum == 201 trick) is 4°Lʒ0å}ʒÇOт·-. Leaving this here, maybe someone can golf it further

@Mr.Xcoder Added it to the answer. Found another 13-bytes alternative as well: ₄4°ŸʒW_sSO9Q*.

₄4°ŸʒD0åôO9Q. Using a single filter is usually shorter.

@Grimy Oh, very smart using the ô to convert the number to a list of digits right away!

Alternative 12: 4°LʒW.$ÇOт·-. Sad .$ is a two-byte built-in.

Nevermind, here's a 10: ₄4°ŸʒW°ö9Q

@Grimy Dang.. I don't even recognize my own answer in it anymore, haha. Very impressive! You've even beat Dennis' 11-byter Jelly answer.

Java 8, 128 117 115 bytes
v->{int i=109,r[]=new int[i],n=i;for(;i>0;n++)if((n+"").chars().sum()==201&(n+"").contains("0"))r[--i]=n;return r;}

-11 bytes thanks to @nwellnhof.
Try it online.
Explanation:
v->{                              // Method with empty unused parameter & int-array return
  int i=109,                      //  Index-integer, starting at 109
      r[]=new int[i],             //  Result-array of size 109
      n=i;                        //  Number integer, starting at 109
   for(;i>0;                      //  Loop as long as `i` is not 0 yet:
       n++)                       //    After every iteration, increase `n` by 1
     if((n+"").chars().sum()==201 //   If the sum of the unicode values of `n` is 201,
                                  //   this means there are four digits, with digit-sum = 9
        &(n+"").contains("0"))    //   and `n` contains a 0:
       r[--i                      //    Decrease `i` by 1 first
            ]=n;                  //    And put `n` in the array at index `i`
  return r;}                      //  Return the array as result

What about chars().sum()==201?

@nwellnhof Ah, of course. Thanks!

R, 85 bytes
(just competing for the best abuse of R square brackets ... :P )
`[`=`for`;i[a<-0:9,j[a,k[a,w[a,if(sum(s<-c(i,j,k,w))==9&any(!s)&i)write(s,1,s='')]]]]

Try it online!

Holy for loops, Batman!

Octave, 49 bytes
6 bytes saved using a more convenient output format as suggested by J.Doe.
Thanks to @Laikoni for a correction.
y=dec2base(x=1e3:9999,10)'-48;x(sum(y)==9>all(y))

Try it online!

I don't know anything about Octave, but it looks like you can leave the disp off...

@J.Doe OP has confirmed that that output format is acceptable. Thanks for the suggestion!

APL(Dyalog), 33 29 bytes
1e3+⍸(0∘∊∧9=+/)¨⍎¨∘⍕¨1e3+⍳9e3

-4 bytes thanks to @Adam
Try it online!

29: 1e3+⍸(0∘∊∧9=+/)¨⍎¨∘⍕¨1e3+⍳9e3

PHP, 69, 87 bytes 74 bytes
for($i=999;$i<9001;$i++){echo((array_sum(str_split($i))==9&strpos($i,"0")!=0)?$i:" ");}
for($i=999;$i++<1e4;)echo!strpos($i,48)|array_sum(str_split($i))-9?" ":$i;
Note this puts a space for every "failed" number, leading to some kind of funky spacing. This can be changed to comma separation, but will add another 4 characters: ?$i.",":""
Got bigger because I wasn't checking for 0. Derp. Shortened by 13 by Titus!

I don't really know PHP, but does this code ensure that each year contains a zero?

This code does not check for zero in the number.

13 bytes shorter: for($i=999;$i++<1e4;)echo!strpos($i,48)|array_sum(str_split($i))-9?" ":$i;

Here´s another byte: ?"$i,":"" er ... now the other way round: ?"":"$i,"

Actually @Titus that adds a couple bytes. We don't need quotes around $i unless we're including a string w/ it.

That´s for the comma separated version: $i."," has six bytes; "$i," has five.

Fair enough, though OP said the huges amount of spaces was fine, so no need for the comma.

Ruby, 46 42 41 bytes
?9.upto(?9*4){|x|x.sum==201&&x[?0]&&p(x)}

Try it online!
How it works:

Iterate on strings ranging from '9' to '9999'
Check that sum of ASCII values is 201
Check if string contains a zero (without regex, a regex would be 1 byte longer)

(Thanks Laikoni for -2 bytes)

9*3 can be just 9, because checking against 201 already requires 4 digit numbers.

Pip, 18 bytes
{0Na&$+a=9}FIm,t*m

Use an ouput-format flag such as -p to get readable output. Try it online!
{0Na&$+a=9}FIm,t*m
             m,t*m  Range from 1000 to 10*1000
{         }FI       Filter on this function:
 0Na                 There is at least one 0 in the argument
    &                and
     $+a             The sum of the argument
        =9           equals 9

Wolfram Language (Mathematica), 56 55 bytes
Select[9!!~Range~9999,Tr@#==Times@@#+9&@*IntegerDigits]

Try it online!
We test the range from 9!! = 945 to 9999, since there are no results between 945 and 999. Maybe there's a shorter way to write a number between 9000 and 10007,  as well.
Tr@#==Times@@#+9& applied to {a,b,c,d} tests if a+b+c+d == a*b*c*d+9, which ends up being equivalent to The Anno Condition.

In retrospect, 9!! isn't any shorter than 999 or something, but it beats 1000.

What is 9!! ? In guessing it isnt related to factorials.

@RobertFraser Double factorial: 9*7*5*3*1.

Dart,  103 100  96 bytes
f()=>List.generate(9001,(i)=>'$i').where((i)=>i.contains('0')&&i.runes.fold(0,(p,e)=>p+e)==201);

-3 bytes by setting the value in the array to string, making the conversion once and not twice
-4 bytes by using runes instead of codeUnits
Pretty self-explanatory. generates a list of 9001 (0-9000) cells with the cell's index as value, filters the ones containing a 0 then the one having an ASCII sum of 201 (The result if all the ASCII characters sum to 9). These conditions implictly include that the year is 4 digits long because using 2 ASCII numbers (and the 0), you cannot reach 201.
Try it on Dartpad!

Welcome to PPCG. :)

Thanks ! Been lurking for a while, can finally participate

Bash (with seq, grep), 39 bytes
seq 0 9 1e4|egrep '([0-4].*){3}'|grep 0

Try it online!

@xobzoo suggested seq 0 9 1e4|awk '/([0-4].*){3}/&&/0/' to save two bytes.

K (ngn/k), 22 bytes
55_&(|/~a)&9=+/a:!4#10

Try it online!

Nice! 55_&9=+/y*|/'~y:!4#10 for 21?

@streetster thanks :) the ' in |/' looks wrong. the result includes 1116, 1125, 1134, etc which are not supposed to be there

APL (Dyalog Unicode), 23 bytes
55↓⍸(×⌿<9=+⌿)10⊥⍣¯1⍳9e3

Try it online!

that use of < is clever. you can make it even shorter with ⎕io←0 and ( )10⊥⍣¯1⍳9e3 -> ( )¨,⍳4⍴10

Scala (76 63 61 56 bytes)
for(n<-0 to 9000;t=n+""if t.sum==201&t.min<49)println(t)

Try it online

Thanks to Laikoni for the suggestions
Two more bytes shed after applying Jo King's comment

Welcome to PPCG! Do you have an idea what needs to be added to the header or footer section to get this code to run on TIO? Try it online!

@Laikoni, didn't know I could run Scala in TIO. Fixed it. Thanks for the comment.

It looks like t.sum==201 works instead of t.map(_.asDigit).sum==9.

You may find our tips for golfing in Scala interesting. E.g. it looks like s"$n" can be n+"" and s"$t " can be t+" ".

Since you're using the sum is 201 trick, the range doesn't need to start at 999

@JoKing, thanks, I updated the answer

Vyxal, 13 bytes
k1k2r'∑9=n0c∧

Try it Online!
Extremely slow.

Tcl, 77 bytes
time {if [incr i]>1e3&[regexp 0 $i]&9==[join [split $i ""] +] {puts $i}} 9999

Try it online!

J, 38 bytes
-1 byte and bug fixed thanks to Quintec
echo(#~(4#10)(0&e.*9=+/)@#:])1e3+i.9e3

Try it online!
Thanks to Laikoni for findng an even bigger bug!

1000 can be 1e3 for a byte saved, right?

(Also, should it be 9e3 instead of 9e4?)

@Quintec Yes, sure. I tried it, but forgot it (golfing on the phone)

@Laikoni  As in my Red answer, I forgot this, sorry! Fixed.

Japt, 16 bytes
Returns an array of digit arrays.
L²õì l4 k_×ª9aZx

Test it

Explanation
L                    :100
 ²                   :Squared
  õ                  :Range [1,L²]
   ì                 :Convert each to a digit array
     l4              :Filter elements of length 4
        k_           :Remove each Z that returns truthy (not 0)
          ×          :  When reduced by multiplication
           ª         :  OR
              Zx     :  When reduced by addition
            9a       :   And subtracted from 9

OP has ruled that digit arrays aren't valid output unfortunately :o(

APL(NARS), 45 chars, 90 bytes
f←{⍵×⍳(0∊x)∧9=+/x←⍎¨⍕⍵}⋄f¨1e3..5e3⋄f¨5e3..9e3

test afther some formatting:
1008  1017  1026  1035  1044  1053  1062  1071  1080  1107  1170  1206  1260
  1305  1350  1404  1440  1503  1530  1602  1620  1701  1710  1800  2007  2016
  2025  2034  2043  2052  2061  2070  2106  2160  2205  2250  2304  2340
  2403  2430  2502  2520  2601  2610  2700  3006  3015  3024  3033  3042  3051
  3060  3105  3150  3204  3240  3303  3330  3402  3420  3501  3510  3600
  4005  4014  4023  4032  4041  4050  4104  4140  4203  4230  4302  4320  4401
  4410  4500
5004  5013  5022  5031  5040  5103  5130  5202  5220  5301  5310  5400  6003
  6012  6021  6030  6102  6120  6201  6210  6300  7002  7011  7020  7101  7110
  7200  8001  8010  8100  9000

possible alternative
r←f;i;x
   r←⍬⋄i←1e3⋄→B
A: r←r,i
B: i+←1⋄→A×⍳(0∊x)∧9=+/x←⍎¨⍕i⋄→B×⍳i≤9e3

Jelly, 13 bytes
ȷ4µṢÄm3Ḍ)ẹ9ṫ4

Try it online!
How?
ȷ4µṢÄm3Ḍ)ẹ9ṫ4 - Link: no arguments
ȷ4            - literal 10^4 = 10000
  µ     )     - for each in range (1,2,3,...,10000): e.g. 3042       or  5211
   Ṣ          -   sort (given an integer makes digits)    [0,2,3,4]      [1,1,2,5]
    Ä         -   cumulative addition                     [0,2,5,9]      [1,2,4,9]
     m3       -   modulo 3 slice (1st,4th,7th...)         [0,9]          [1,9]
       Ḍ      -   convert from decimal digits             9              19
         ẹ9   - 1-based indices equal to nine             [9,99,999,1008,1017,...,8100,9000]
           ṫ4 - tail from the 4th index                   [1008,1017,...,8100,9000]

Stax, 13 bytes
ü┌o☻≈;╫▄mI░↨Z

Run and debug it
Unpacked, ungolfed, and commented, it looks like this.
VM      Constant one million
        this program produces its output pretty quickly, but takes much longer to end
f       filter the numbers [1..n] using the rest of the program as a predicate
  $     convert to ascii decimal string e.g. "1234"
  |+    sum of ascii codes
  201=  is equal to 201 [result a]
  _E:*  product of the decimal digits [result b]
  >     [result a] is greater than [result b]

Run this one

@Laikoni: I read the challenge wrong, and missed that requirement.  I've updated my submission, at the cost of 4 bytes.

MathGolf, 16 13 bytes
♫♪↨Ç{▒ε*\Σ8=┌

Try it online!
Explanation:
  ↨              Range from
♫♪               1000 to 10000
   Ç{            Filter out by
     ▒ε*         The product of all digits is not 0
        Σ8=┌     The digit sum of the loop index (0 based) is not equal to 8

Does MathGolf have an ord command to transform characters/digits to unicode values? If yes, you might be able to use the same ord sum == 201 trick other answers use, which covers both the length == 4 and digit sum == 9 at the same time.

@KevinCruijssen Unfortunately, it doesn't, but I've requested it

@JoKing I'm a bit late to the party, but both of these things have been implemented, and have been submitted for pulling to TIO. check out the README for instructions. I've had a very stressful month, but I'm trying to get everything implemented.

JavaScript (SpiderMonkey), 57 bytes
for(y=0;y<9e3;)/(?=([0-4].?){3}).*0/.test(y+=9)&&print(y)

Try it online!

JavaScript (SpiderMonkey), 58 bytes
for(y=999;++y<1e4;9-p-q-r-s|q*r*s||print(y))[p,q,r,s]=''+y

Try it online!
You may change print to alert to test it in your browser. :)

Maybe the first time I realize that submitting a whole program is even shorter than a function in JavaScript...

K (oK), 37 bytes
x@&{9=+/x*0in x}'(4#10)\'x:1000+!9000

Try it online!
On mobile, will add explanation later. Couldn't get 0 in' to work without the lambda which wastes bytes...

36 - x@&9=+/+y*|/~+y:(4#10)\'x:1000+!9000

Common Lisp, 140 bytes
(defun x()(loop for x from 1008 to 9000 for y =(map'list #'digit-char-p(prin1-to-string x))when(and(member 0 y)(=(apply #'+ y)9))collect x))

Try it online!

This does not ensure that each year contains at least one zero.

Edited, i must have overlooked that

It looks like you can drop the space in map 'list. Also there are two trailing newlines that do not appear to be necessary.

Julia 1.0, 52 bytes
f()=[x for x=1:9^5 if'0' in"$x"&&sum(Int,"$x")==201]

Try it online!

K (ngn/k), 33 bytes
t@&"0"=*'t@'<'t:$&201=+/'i:$!9001

Try it online!
Will edit with explanation later.

C (clang), 95 91 bytes
f(i){char*c;for(i=1e4;--i;*c+c[1]+c[2]+c[3]-201||index(c,48)&&puts(c))asprintf(&c,"%d",i);}

Try it online!
-4 bytes thanks to @ceilingcat

Haskell, 66 79 74 bytes
drop 55[x>>=show|x<-sequence.replicate 4$[0..9],sum x==9,elem 0x]

Damn you with your type juggling, you can treat numbers as strings :(
Basically just an exact description of whats asked here. Just no spaces.
Try it online! (Modifications for it to run were necessary, tio doesn't support pure functions in Haskell.)

Nice approach, but unfortunately lists of digits are not valid output. However, there a few ways you can save some bytes to make up for the additional ones you need for the final conversion to a string. First of all, a list comprehension is shorter than filter and you can drop the second space in elem 0 x.

A few more: Instead of using map the conversion to string can be put into the list comprehension. concat.map is common enough that there exists a build-in called concatMap, however the overloading of the bind operator =<< on lists has the same effect and is even shorter.

The default bind operator is (>>=), what you showed is the reverse bind operator.

Retina 0.8.2, 52 bytes

9001$*
.
$.`=>$.`;
>|\G\d
$*
M!`\d{4}(?==1{9};)
G`0

Try it online! Explanation:

9001$*

Insert 9001 1s.
.
$.`=>$.`;

Replace each 1 with the number of 1s to its left, followed by =>, followed by the same number again, followed by ;.
>|\G\d
$*

Convert > and subsequent digits to unary, thus calculating the digit sum.
M!`\d{4}(?==1{9};)

List all the 4-digit numbers with a digit sum of 9.
G`0

Keep those that contain a zero.

Jelly, 13 bytes
ȷ4ȷrDS9Ƒ>ẠƲƇḌ

Try it online!
-2 (yucky, yucky trivial) bytes thanks to Dennis.

Red, 109 bytes
repeat n 8000[s: 0 p: 1 foreach t form d: 1000 + n[s: s + r: do t - 48 p: p * r]if all[s = 9 p = 0][print d]]

Try it online!

@Laikoni Sorry, apparently I've missed that part. Fixed.

Pyth, 22 19 bytes
f&}\0Tq201sCMT`M^T4

Output is an array of strings. Try it online here.
f&}\0Tq201sCMT`M^T4
              `M^T4   Array of strings of numbers 0-999
f                     Keep the elements of the above, as T, where:
           CMT          Get the code points of each character in T
          s             Take the sum
      q201              Does the above == 201?
  }\0T                  Does T contain "0" ?
 &                      Logical AND the two previous results

The 201 trick is the same as used in other answers.
Previous answer, without using 201 trick, 22 bytes: iRTf}0Tfq9sTjRTr^T3^T4

PHP, 114 bytes
<?php
for($i=1e3;$i<=9e3;$i++)
    if(array_sum($a=[$i/1e3%10,$i/100%10,$i/10%10,$i%10])==9&in_array(0,$a))
        echo $i.' ';

Try it online!

Prolog 117 bytes
b(X):-between(0,9,X).
w(X):-write(X).
y():-b(A),b(B),b(C),b(D),X is A+B+C+D,X=9,(A=0;B=0;C=0;D=0),w(A),w(B),w(C),w(D).

Would you consider adding a link to a service like Try it online! - (link is to your code, but I'm not sure if the footer is right since I don't prolog much.)?

98

91

J, 35 bytes
echo@>55}.I.(4#10)(*/<9=+/)@#:i.1e4

Try it online!

C# (.NET Core), 106 bytes
for(int i=999;++i<9999;)if(i.ToString().Sum(c=>c-'0')==9&&i.ToString().Contains("0"))Console.WriteLine(i);

Try it online!
Ungolfed:
for(int i = 999; ++i < 9999;)               // from 1000 to 9999 (fulfills four digit parameter)
    if(i.ToString().Sum(c => c - '0') == 9  // if the sum of the digits is nine
            && i.ToString().Contains("0"))  //   and if the year contains at least one zero
        Console.WriteLine(i);               // write the year to the console

Perl 5 -MList::Util=sum, 40  32 bytes
map/0/*9-(sum/./g)||say,1E3..9E3

Try it online!

Scala, 58 bytes
jrook's solution is currently 2 byte shorter
val v=(9 to 9000).filter(x=>(x+"").min<49&(x+"").sum==201)

val v=(9 to 9000).map(_+"").filter(x=>x.min<49&x.sum==201)

Try it online!

Attache, 37 bytes
{{Sum@Ords@_=201and"0"in _}@S\1:9000}

Try it online!
Based on the perl answer.

Kotlin, 59 bytes
Lambda that returns a List<Int> containing every valid year.
{(1008..9000).filter{"$it".sumBy{it-'0'}==9&&'0' in "$it"}}

Try it online!

Deadfish~
{iiiii}dcdcc{i}ddc{d}iiicdciciiiiiicddddddcdciiciiiicdddddcdciiiciicddddcdciiiiccdddcdciiiiicddcddcdciiiiiicddddcdcdc{i}dddcddddddccdc{i}ddc{d}iiciccdc{i}dddcddddddcciiiiiic{d}iiicicicddciiiiiicdddddciciiiicddddddciciicdddciiiiicddddciiciicdddddciciiicddddciiiicdddciiiccddddciciiiicdddddciiicddciiiicddcdddciciiiiicddddddciicdciiiiicddddcddciciiiiiic{d}iiicicciiiiiicddddddcdcic{i}dddc{d}iicciicddcc{i}dddcdddddcddciciiiiicddddcddciiciiicdddcddciiicicddcddciiiicdcdcddciiiiicdddccddciiiiiicdddddcicddc{i}dddc{d}iiiciicdcdciiiiiicddddcdciiiiicddddddciiccddciiiiicdddcciiicdddddciicicdddciiiicddcicicddddciiciicddddciiicdciicdcdddciiciiicdddddciicciiicdddcddciiciiiicddddddciciciiiicdddddcdciiciiiiic{d}iiicciiicdddcciiiiiicdddcdddciciiiicddcdddciiciicdcdddciiicccdddciiiicddcicdddciiiiicddddciicdddciiiiiicddddddciiicddcdciiiiicddcddciiiicdddddciiicdcddciiiicdcdciicddddciiiccdddciiiccccdddciiicicddddciicicicddcddciiiciicdddddciciiciicddddcdciiiciiicddddddcciiiicddddcciiiiicdcddddciciiiccddddciicicicddddciiicdciicddddciiiicdddciiicddddciiiiicdddddciiiicdddcdciiiiccdddciiicddddciiiicddcddciiicicddcicdddciiiicdcdddciiciicdcdcddciiiiccddddciciiiccdddcdciiiicicdddddcciiiiicdddddcciiiicicdddddciciiciicdddddciicciiicdddddciiicddciiiicdddddciiiicddddciiiiicddddcdciiiciicddddciicdddciiiiicdddcddciiciiicdddccddciiiiicddcdddciciiiicddcddcdciiiiicdcddddcciiiiiicddddddcciiiciiicddddddciciciiiicddddddciicdciiiiicddddddciiicdddciiiiiicdddddcdciiciiiicdddddcicddciiiiiicddddcddciciiiiicddddcdcdciiiiiicdddcdddcc{i}dddc{d}iiicciiciiiiic{d}iiicicciiiiiic{d}iiiciicddc{i}dddcddddddcdciciiiiiicddddddccdc{i}dddcdddddcddcc{i}ddc{d}iiccic{i}dddc{d}iicicdc{i}ddc{d}iiicdcc{i}dc{d}iccc

Do I need the spaces?

Yes, there should be some delimiter between the years such that they are easily recognizable.

Also please add the byte count of your answer to the header and, if you want, add a TIO link: Try it online!

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
A Semi-palindrome Puzzle
An palindrome is a word that is its own reverse.
Now there are some words that might look like palindromes but are not. For example consider the word  sheesh,  sheesh is not a palindrome because its reverse is hseehs which is different, however if we consider sh to be a single letter, then it's reverse is sheesh.  This kind of word we will call a semi-palindrome.
Specifically a word is a semi-palindrome if we can split up the word in to some number of chunks such that when the order of the chunks are reversed the original word is formed.  (For sheesh those chunks are sh e e sh)  We will also require no chunk contains letters from both halves of the word (otherwise every word would be a semi-palindrome).  For example rear is not a semi-palindrome because r ea r has a chunk (ea) that contains letters from both sides of the original word.  We consider the central character in an odd length word to be on neither side of the word, thus for words with odd length the center character must always be in it's own chunk.
Your task will be to take a list of positive integers and determine if they are a semi-palindrome.  Your code should output two consistent unequal values, one if the input is a semi-palindrome and the other otherwise.  However the byte sequence of your code must be a semi-palindrome itself.
Answers will be scored in bytes with fewer bytes being better.
Test-cases
[] -> True
[1] -> True
[2,1,2] -> True
[3,4,2,2,3,4] -> True
[3,5,1,3,5] -> True
[1,2,3,1] -> False
[1,2,3,3,4,1] -> False
[11,44,1,1] -> False
[1,3,2,4,1,2,3] -> False

Program to generate more testcases.

borrible pointed out that these are similar to generalized Smarandache palindromes.  So if you want to do some further reading that's one place to start.

Why did you define semi-palindromes using strings but your inputs are arrays of integers? In addition to being confusing, this means we can't test our source code using our own program.

@BradC Palindromes and the like are often explained in terms of words, since it's a bit easier to do so.

@BradC Strings tend to introduce weird edge cases, particularly in terms of characters vs bytes.  I choose number because they are simpler.  I thought words would be easier for explanation purposes.

These types of palindromes are known as Generalized Smarandache Palindromes in the literature.

@borrible It looks like while GSP is a similar concept it is not quite the same thing.  For example $56555$ is a GSP but is not a "semi-palindrome".  The difference is that "semi-palindromes" have the rule that groupings cannot cross the center while GSP has the rule that there must be at least 2 groupings.  Interesting to know none the less.

So seems to me that for palindrome it has to return true... right?

@RosLuP Yes, "true" palindromes are also semi-palindromes, just treat each character/integer as-is with no additional "chunking".

The previous version of my answer failed on the input [441,2,144], that might be a good test case.

Relevant, IMO

Retina 0.8.2, 85 69 bytes
M`^(.+,)*(\d+,)?(?<-1>\1)*$(?(1)^)|M`^(.+,)*(\d+,)?(?<-1>\1)*$(?(1)^)

Try it online! Explanation:
M`

Selects Match mode. In fact, Retina defaults to Match mode for a single-line program, but the second copy of the code would always match if not for these extra characters.
^

Match must start at the beginning.
(.+,)*

Capture a number of runs of characters. Each run must end in a comma.
(\d+,)?

Optionally match a run of digits and a comma.
(?<-1>\1)*

Optionally match all of the captures in reverse order, popping each one as it is matched.
$

Match must end at the end.
(?(1)^)

Backtrack unless all of the captures were popped. It works by requiring the match to still be at the start of the string if we have an unpopped capture, which is impossible.

Jelly, 27 23 bytes
ṖUṁ@Ƒ€ṚẸHḢŒŒHḢŒṖUṁ@Ƒ€ṚẸ

Returns 1 for semi-palindromes, 0 otherwise.
Try it online!
How it works
ṖUṁ@Ƒ€ṚẸHḢŒŒHḢŒṖUṁ@Ƒ€ṚẸ  Main link. Argument: A (array)

          Œ              Invalid token. Everything to its left is ignored.
           ŒH            Halve; divide A into two halves similar lengths. The middle
                         element (if there is one) goes into the first half.
             Ḣ           Head; extract the first half.
              ŒṖ         Generate all partitions of the first half.
                U        Upend; reverse each chunk of each partition.
                         Let's call the result C.

                     Ṛ   Yield R, A reversed.
                   Ƒ€    Fixed each; for each array P in C, call the link to the left
                         with arguments P and R.
                         Return 1 if the result is P, 0 if not.
                 ṁ@          Mold swapped; replace the n integers of C, in reading
                             order, with the first n integers of R.
                     Ẹ   Exists; check if one of the calls returned 1.

05AB1E, 59 47 43 41 bytes
2äøø€.œ`âʒ`RQ}gĀIg_^q2äøø€.œ`âʒ`RQ}gĀIg_^

-12 bytes thanks to @Emigna.
Try it online or verify all test cases.
Explanation:
2ä               # Split the input into two parts
                 #  i.e. [3,4,2,0,2,3,4] → [[3,4,2,0],[2,3,4]]
  øø             # Zip twice without filler
                 # This will remove the middle item for odd-length inputs
                 #  i.e. [[3,4,2,0],[2,3,4]] → [[3,2],[4,3],[2,4]] → [[3,4,2],[2,3,4]]
    €.œ          #  Then take all possible partitions for each inner list
                 #   i.e. [[3,4,2],[2,3,4]]
                 #    → [[[[3],[4],[2]],[[3],[4,2]],[[3,4],[2]],[[3,4,2]]],
                 #       [[[2],[3],[4]],[[2],[3,4]],[[2,3],[4]],[[2,3,4]]]]
`                # Push both lists of partitions to the stack
 â               # Take the cartesian product (all possible combinations) of the partitions
                 #  i.e. [[[[3],[4],[2]],[[2],[3],[4]]],
                 #        [[[3],[4],[2]],[[2],[3,4]]],
                 #        ...,
                 #        [[[3,4,2]],[[2,3,4]]]]
  ʒ   }          # Filter this list of combinations by:
   `             #  Push both parts to the stack
    RQ           #  Check if the second list reversed, is equal to the first
                 #   i.e. [[3,4],[2]] and [[2],[3,4]] → 1 (truthy)
       gĀ        # After the filter, check if there are any combinations left
                 #  i.e. [[[[3,4],[2]],[[2],[3,4]]]] → 1 (truthy)
         Ig_     # Check if the length of the input was 0 (empty input-list edge-case)
                 #  i.e. [3,4,2,0,2,3,4] → 7 → 0 (falsey)
            ^    # Bitwise-XOR
                 #  i.e. 1 XOR 0 → 1 (truthy)
             q   # Stop the program (and then implicitly output the top of the stack)
2äøø€.œ`âʒ`RQ}gĀIg_^
                 # Everything after the `q` are no-ops to comply to the challenge rules

You can get around the issue with odd-length lists with 2äøøε.œ}`, saving 6 bytes. You also seem to have left 30 unused bytes in...

@Emigna the no-ops at the end are to comply with the challenge's restricted source requirement

@KamilDrakari: Oh right. Forgot that part. Good news is that the 6-byte save will be 12 bytes then :)

@Emigna Very smart with the double-zip trick. I was not happy about that part, but this is a lot better! Btw, since the Elixir rewrite the 2-byte commands can be used with € instead of ε  }. :)

@KevinCruijssen: Ah cool. I didn't know that.

Python 2, 157 153 147 143 bytes
-4 bytes thanks to tsh.
s=lambda x,i=0:len(x)<2or[]<x[i:]and(x[-i:]==x[:i])&s(x[i:-i])|s(x,i+1)
s=lambda x,i=0:len(x)<2or[]<x[i:]and(x[-i:]==x[:i])&s(x[i:-i])|s(x,i+1)

Try it online!

Change x==x[::-1] to len(x)<2 save 2*2 bytes; 143 bytes

Jelly,  33  32 bytes
-1 Thanks to Erik the Outgolfer
Thanks also to Dennis for a bug fix and looking into changing an implementation detail in Jelly.
ẸƇŒḂƇƊ$ƊĊHṀċÄẈṖŒŒṖẈÄċṀHĊƊ$ƊƇŒḂƇẸ

Semi-palindromes yield 1, others yield 0.
Try it online! (slow as it's \$O(2^n)\$ in input length)
Or see the test-suite.
The only chunks are the ŒḂs ({3rd & 4th} vs {29th & 30th} bytes), just to allow the code to parse.
How?
All the work is performed by the right-hand side - the "Main Link":
ŒṖẈÄċṀHĊƊ$ƊƇŒḂƇẸ - Main Link: list
ŒṖ               - all partitions
           Ƈ     - filter keep those for which this is truthy (i.e. non-zero):
          Ɗ      -   last three links as a monad:
  Ẉ              -     length of each
         $       -     last two links as a monad:
   Ä             -       cumulative addition
        Ɗ        -       last three links as a monad:
     Ṁ           -         maximum
      H          -         halve
       Ċ         -         ceiling
    ċ            -     count
              Ƈ  - filter keep those for which this is truthy:
            ŒḂ   -   is palindrome?
               Ẹ - any?

05AB1E, 37 bytes
Utilizes roughly the same technique Jonathan came up with.
.œʒ€gηOZ;îå}εÂQ}ZĀqĀZ}QÂε}åî;ZOηg€ʒ.œ

Try it online!

.œʒ€gηOZ;îå}εÂQ}ZĀqĀZ}QÂε}åî;ZOηg€ʒ.œ

Full program. Receives a list from STDIN, outputs 1 or 0 to STDOUT.
.œʒ        }

Filter-keep the partitions that satisfy...
   €gηOZ;îå

This condition: The lengths of each (€g) are stored in a list, whose prefixes (η) are then summed (O), hence giving us the cumulative sums of the lengths list. Then, the ceiled halve of the maximum of that list is pushed onto the stack – but keeping the original list on it as well (Z;î) and if it occurs (å) in the cumulative sums then the function returns truthy.
εÂQ}

For each, compare (Q) a with a reversed, which are pushed separately on the stack by Â. Returns a list of 0s and 1s.
ZĀq

Maximum. If any is truthy, then 1 else 0. End execution. Everything that follows is completely ignored.

Perl 6, 87 79 bytes
-8 bytes with some tricks from Jo King's answer
$!={/\s/&&/^(.+)\s[(.+)\s]*$0$/&&$1.$!}#$!={/\s/&&/^(.+)\s[(.+)\s]*$0$/&&$1.$!}

Try it online!
Port of tsh's JavaScript answer. Returns two different Regex objects.

Python 2, 275 251 205 bytes
-24 bytes thanks to @KevinCruijssen
-44 bytes thanks to @PostLeftGhostHunter
-2 more bytes thanks to @KevinCruijssen

def s(x):
 l=len(x)
 if l<2:return 1>0
 for i in range(1,l/2+1):
	if x[l-i:]==x[:i]:return s(x[i:l-i])
def s(x):
 l=len(x)
 if l<2:return 1>0
 for i in range(1,l/2+1):
	if x[l-i:]==x[:i]:return s(x[i:l-i])

Returns True for semi-palindrome, None otherwise
Try it online!

Or just return 1

Why s(x) defined twice?

Because they say count as palindrome... but is possible define one function with the same name???

@RosLuP Yes you can. The second one just overwrites the first

Ruby, 129 bytes
f=->l{!l[1]||(1...l.size).any?{|x|l[0,x]==l[-x,x]&&f[l[x..~x]]}}#f=->l{!l[1]||(1...l.size).any?{|x|l[0,x]==l[-x,x]&&f[l[x..~x]]}}

Try it online!

Japt, 66 bytes

@¯X eUsXn}a1 "
Ê§V?UÊ<2:ßUéV sVÑ
@¯X eUsXn}a1 "
Ê§V?UÊ<2:ßUéV sVÑ

Japt Interpreter
Big improvement this version, it actually beats most of the practical languages now. Now operates on an array of integers since the previous method had a bug.
Explanation:
@        }a1         Find the first number n > 0 such that...
 ¯X                   the first n elements
     UsXn             and the last n elements
    e                 are the same

"
Ê§V?UÊ<2:ßUéV sVÑ    String literal to make it a Semi-palindrome
@¯X eUsXn}a1 "

Ê§V?                 If n >= length of input...
    UÊ<2              return true if the length is less than 2
        :            Otherwise...
          UéV         move n elements from the end of the input to the start
              sVÑ     remove the first 2*n elements
         ß            and repeat on the remaining elements

JavaScript (Node.js), 139 bytes
f=a=>(s=(''+a).replace(/^(.*),((.*),)?\1$/,'$3'))!=a?f(s):/,/.test(s)//^(.*),((.*),)?\1$/,'$3'))!=a?f(s):/,/.test(s)f=a=>(s=(''+a).replace(

Try it online!

C (gcc) (X86), 216 bytes
p(L,a,n)int*a;{return n?(memcmp(a,a+L-n,n*4)|p(L-2*n,a+n,L/2-n))&&p(L,a,n-1):1<L;}
#define p(L,a)p(L,a,L/2)//p(L,a,n)int*a;{return n?(memcmp(a,a+L-n,n*4)|p(L-2*n,a+n,L/2-n))&&p(L,a,n-1):1<L;}
#define p(L,a)p(L,a,L/2)

Try it online!
p(L,a,n) returns 0 if array a of length L is a semi-palindrome, 1 otherwise. Given that all prefixes of length >n are already checked, it compares the prefix of length n with the suffix of length n. p(L,a) is the entry point.
Unfortunately, the more interesting solution is longer:
224 bytes
(f(L,a,n))//#define p(L,a)(n=L/2,
int*a,n;
{return n?(memcmp(a,a+L-n,n*4)|f(L-2*n,a+n,L/2-n))&&f(L,a,n-1):1<L;}//{return n?(memcmp(a,a+L-n,n*4)|f(L-2*n,a+n,L/2-n))&&f(L,a,n-1):1<L;}
int*a,n;
#define p(L,a)(n=L/2,f(L,a,n))//(

Try it online!
Ungolfed:
(f(L,a,n)) //#define p(L,a)(n=L/2,
int*a,n;
{
  return n
    ? (memcmp(a, a+L-n, n*4) | f(L-2*n, a+n, L/2-n)) &&
      f(L,a,n-1)
    : 1 < L;
} // { ... }
int*a,n;
#define p(L,a)(n=L/2,f(L,a,n)) //(

Perl 6, 81 bytes
($!={/../&&/^(.+)(.*)$0$/&&$1.$!})o&chrs#($!={/../&&/^(.+)(.*)$0$/&&$1.$!})o&chrs

Try it online!
Returns the regex /../ for True and the regex /^(.+)(.*)$0$/ for False. Works similarly to nwellnhof's answer, but converts the list to a string beforehand.

Brachylog, 21 bytes
ḍ{|b}ᵗ~cᵐ↔ᵈḍ{|b}ᵗ~cᵐ↔

Try it online!
ḍ              Split the input in half, with the second half longer if uneven.
 {|b}ᵗ         Maybe remove the first element of the second half.
      ~cᵐ      Partition both halves.
         ↔ᵈ    Is it possible for one to be the other's reverse?
ḍ{|b}ᵗ~cᵐ↔     The remainder of the program always succeeds.

PHP 237 bytes
function f($a){for($x=2>$c=count($a);++$i<=$c/2;)$x|=($s=array_slice)($a,0,$i)==$s($a,-$i)&f($s($a,$i,-$i));return$x;}#function f($a){for($x=2>$c=count($a);++$i<=$c/2;)$x|=($s=array_slice)($a,0,$i)==$s($a,-$i)&f($s($a,$i,-$i));return$x;}

recursive function, returns true (for input containing less than two elements) or 1 for truthy,
0 for falsy.   Try it online (contains breakdown).
Actual code length is 118 bytes;
semi-palindrome created via code duplication.
For better performance, replace & with && and insert !$x&& before ++$i.

Scala, 252 bytes
def^(s:Seq[Int]):Int={val l=s.size;if(l>1)(1 to l/2).map(i=>if(s.take(i)==s.takeRight(i))^(s.slice(i,l-i))else 0).max else 1}//def^(s:Seq[Int]):Int={val l=s.size;if(l>1)(1 to l/2).map(i=>if(s.take(i)==s.takeRight(i))^(s.slice(i,l-i))else 0).max else 1}

Try it online!
PS. Apparently, solution is 2 times longer just to satisfy a requirement that source code is semi palindrome as well.
PPS. Not a code-golf candidate but purely functional solution using pattern matching:
  def f(s:Seq[Int], i:Int=1):Int = {
    (s, i) match {
      case (Nil ,_) => 1
      case (Seq(_), _) => 1
      case (l, _) if l.take(i) == l.takeRight(i) => f(l.slice(i,l.size-i), 1)
      case (l, j) if j < l.size/2 => f(l, i+1)
      case (_, _) => 0
    }
  }

The challenge requires that your code be a semi-palindrome as well.  That's most the fun in the challenge.

@PostLeftGhostHunter, I added original source code into comment to satisfy the requirement. BTW, what is the fun of making source code semi palindrome? If I'm not wrong, every solution in this thread would be two times shorter without this requirement. Are you aware of any solution not like that?


text:
Print a Wavy String Line-by-Line
Challenge
Write a program or function that takes in a string s and integer n as parameters. Your program should print (or return) the string when transformed as follows:
Starting in the top-left and moving down and to the right, write s as a wave of height n. Then, from top to bottom, combine each row as a string (without spaces).
Example
Given the string "WATERMELON" and a height of 3:
The wave should look like this:
W   R   O
 A E M L N
  T   E

Then, combine the rows from top to bottom:
WRO
AEMLN
TE

So, your program should return the string "WROAEMLNTE"
Likewise, "WATERMELON" with height 4 should produce the following wave:
W     E
 A   M L
  T R   O
   E     N

Your program should then return the string "WEAMLTROEN"
Rules
Input
Input can be taken in any reasonable format. The string can be in any case you prefer. You may assume that 0 < n <= s.length
Output
Output should consist only of the transformed string (whether returned or printed to STDOUT), plus any trailing newlines.
Scoring
This is code-golf, so shortest answer in bytes wins! Standard loopholes are not allowed.
Test Cases
Input                        Output

programmingpuzzles, 5 ->     piermnlsomgzgapzru
codegolf, 3           ->     cgoeofdl
elephant, 4           ->     enlatehp
1234567, 3            ->     1524637
qwertyuiop, 1         ->     qwertyuiop

Can we assume n>1? Please clarify and if not add a test case

Note that at least three current answers fail for n=1. You would have to notify the users if that case needs to be supported

You may assume n > 0, but n=1 is a valid case. I will update the question now.

Has this been in the sandbox for a while? It looks so familiar for a reason!.. :S.. Loosely related: Zig-zag Strings and Draw the Sawtooth Alphabet.

I checked those questions and while they are similar, I believe my question is distinct because the others require printing the pattern itself, while this question only uses the pattern as a method to determine how to manipulate the string. I think those questions are more ASCII art based, while this is based just on string manipulation.

@Cowabunghole I know. :) Related only means it's somewhat similar and existing answers there might be helpful for this challenge. I only mention it to have them appear at the linked questions at the right. Related != duplicated. ;)

I've never seen a rail fence cipher encoded with only one rail. Just sayin'

@wooshinyobject Wow, I actually had no idea there was a name for this! I guess for the purposes of hiding text, 1 rail would be pretty useless. Hopefully it at least makes for a more interesting code golf problem :)

possible duplicate of half of this old challenge; the specs here are much more in line with what we've come to expect, though, so I wouldn't close this as a dupe.

@Giuseppe Ah, that's the one!! No doubt the rail fence cipher looked familiar, I've done that other challenge once..

@wooshinyobject You see 1-rail ciphers all the time. That just means it's written normally.

@Veskah Ah yes, the old double rot13 trick.

Husk, 6 bytes
δÖK…¢ḣ

Try it online!
Works for n = 1 as well.
Explanation
δÖK…¢ḣ  Implicit inputs, say n=4 and s="WATERMELON"
     ḣ  Range: [1,2,3,4]
    ¢   Cycle: [1,2,3,4,1,2,3,4,1,2,3,4..
   …    Rangify: [1,2,3,4,3,2,1,2,3,4,3,2..
δÖK     Sort s by this list: "WEAMLTROEN"
        Print implicitly.

The higher order function δ works like this under the hood.
Suppose you have a higher order function that takes a unary function and a list, and returns a new list.
For example, Ö takes a function and sorts a list using it as key.
Then δÖ takes a binary function and two lists, zips the lists together, applies Ö to sort the pairs using the binary function as key, and finally projects the pairs to the second coordinate.
We use K as the key function, which simply returns its first argument and ignores the second.

MATL, 16 bytes
Zv3L)t?yn:)2$S}i

Try it online! Or verify all test cases.
Explanation
Consider inputs 5, 'programmingpuzzles'.
Zv     % Input, implicit: number n. Symmetric range
       % STACK: [1 2 3 4 5 4 3 2 1]
3L     % Push [1 -1+1j]. When used as an index, this means 1:end-1
       % STACK: [1 2 3 4 5 4 3 2 1], [1 -1+1j]
)      % Index. Removes last element
       % STACK: [1 2 3 4 5 4 3 2]
t      % Duplicate
       % STACK: [1 2 3 4 5 4 3 2], [1 2 3 4 5 4 3 2]
?      %   If non-empty and non-zero
       %   STACK: [1 2 3 4 5 4 3 2]
  y    %   Implict input: string s. Duplicate from below
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], 'programmingpuzzles'
  n    %   Number of elements
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], 18
  :    %   Range
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], [1 2 3 ··· 17 18]
  )    %   Index modularly
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 1 2]
  2$S  %   Two-input sort: stably sorts first input as given by the second
       %   STACK: 'piermnlsomgzgapzru'
}      % Else. This branch is entered when n=1. The stack contains an empty array
       %   STACK: []
  i    %   Take input
       %   STACK: [], [], 'programmingpuzzles'
       % End, implicit
       % Display stack, implicit. Empty arrays are not displayed

J, 54, 29, 27 26 bytes
-1 byte thanks to hoosierEE
([\:#@[$[:}:|@i:@<:@]) ::[

Try it online!

@LuisMendo Hmm, once again I missed something important. Thanks! Fixed.

I initially missed it too, then realized and asked the OP. There should have been a test case with n=1 since the beginning

|@i: instead of [:|i: saves a byte

@hoosierEE Yes, thanks!

R, 68 bytes
function(s,n)intToUtf8(unlist(split(utf8ToInt(s),-(n:(2.9-n)-1)^2)))

Try it online!

-10 bytes thanks to @Giuseppe
-17 bytes because I was silly
-9 bytes and n=1 case fixed thanks to @J.Doe
-3 bytes thanks to @JayCe

Haskell, 64 bytes
s#n=[c|m<-[0..n],(c,i)<-zip s.cycle$[0..n-1]++[n-2,n-3..1],i==m]

Try it online!

Python 2, 119 108 98 92 91 97 93 91 90 bytes
lambda s,n:''.join(c*(j%(2*n-2or 1)in(i,2*n-i-2))for i in range(n)for j,c in enumerate(s))

Try it online!
-1 byte, thanks to Jonathan Frech

Possible 90 bytes.

05AB1E (legacy), 11 8 bytes
Σ²Lû¨¾è¼

Inspired by @LuisMendo's MATL answer.
-3 bytes thanks to @Adnan
Try it online. (No test suite, because the legacy version of 05AB1E didn't had a reset for the counter_variable.)
Explanation:
Σ           # Sort the (implicit) input-string by:
 ²L         #  Create a list in the range [1, second input-integer]
            #   i.e. 5 → [1,2,3,4,5]
   û        #  Palindromize it
            #   i.e. [1,2,3,4,5] → [1,2,3,4,5,4,3,2,1]
    ¨       #  Remove the last item
            #   i.e. [1,2,3,4,5,4,3,2,1] → [1,2,3,4,5,4,3,2]
     ¾è     #  Index into it (with wraparound) using the counter_variable (default 0)
            #   i.e. counter_variable = 0 → 1
            #   i.e. counter_variable = 13 → 4
       ¼    #  And after every iteration, increase the counter_variable by 1

NOTE: The counter_variable is used, because in the Python Legacy version of 05AB1E, the Σ didn't had an index N, which it does have in the new Elixir rewrite version of 05AB1E. So why do I still use the Legacy version? Because in the Elixir rewrite it has a bug and doesn't sort for \$n=1\$, which would all result in an empty list [] after the indexing. All other test cases work as intended with Σ²Lû¨Nè: try it online, but the test case with \$n=1\$ times out without result, even with --no-lazy argument flag: try it online.

You don't need the ¹g∍ part since 05AB1E uses cyclic indexing for è.

@Adnan Ah, I'm an idiot.. >.> Thanks!

Japt, 16 bytes
¬üÏu´VÑ aV°ÃÔc q

Test it online!
Explanation
 ¬ üÏ   u´ VÑ  aV° Ã Ô c q
Uq üXY{Yu--V*2 aV++} w c q    Ungolfed
                               Implicit: U = input string, V = size of wave
Uq                             Split U into chars.
   üXY{            }           Group the items in U by the following key function:
       Y                         Take the index of the item.
        u--V*2                   Find its value modulo (V-1) * 2.
               aV++              Take the absolute difference between this and (V-1).
                                 This maps e.g. indices [0,1,2,3,4,5,6,7,...] with V=3 to
                                                        [2,1,0,1,2,1,0,1,...]
                                 The items are then grouped by these values, leading to
                                 [[2,6,...],[1,3,5,7,...],[0,4,...]].
                     w         Reverse the result, giving [[0,4,...],[1,3,5,7,...],[2,6,...]].
                       c       Flatten.
                         q     Join back into a single string.

o.O That ü method is new?

Yep, added on Saturday :-)

You could take input as a character array to save a byte and output one or use the -P flag to save another 2.

JavaScript (ES6), 75 bytes
Shorter formula suggested by @MattH (-3 bytes)
Takes input as (string)(n).
s=>n=>--n?[...s].map((c,x)=>o[x=x/n&1?n-x%n:x%n]=[o[x]]+c,o=[])&&o.join``:s

Try it online!

JavaScript (ES7), 78 bytes
Saved 4 bytes thanks to @ETHproductions
Takes input as (string)(n).
s=>n=>--n?[...s].map((c,x)=>o[x=n*n-(x%(n*2)-n)**2]=[o[x]]+c,o=[])&&o.join``:s

Try it online!

My solution ended up being pretty similar to yours. You can save -3 bytes calculating the insert index of o with x/n&1?n-x%n:x%n instead of n*n-(x%(n*2)-n)**2.

@MattH Nicely done. Thanks!

Jelly, 8 bytes
6 byter fails for height 1; two bytes used to address it ...maybe a 7 can be found?
ŒḄṖȯ1ṁỤị

A dyadic link accepting a positive integer and a list of characters which yields a list of characters.
Try it online!
How?
ŒḄṖȯ1ṁỤị - Link: positive integer N; list of characters, T
ŒḄ       - bounce (implicit range of) N -> [1,2,3,...,N-1,N,N-1,...,3,2,1]
  Ṗ      - pop off the final entry         [1,2,3,...,N-1,N,N-1,...,3,2]
   ȯ1    - OR one                          if this is [] get 1 instead
     ṁ   - mould like T (trim or repeat to make this list the same length as T)
      Ụ  - grade-up (get indices ordered by value - e.g. [1,2,3,2,1,2] -> [1,5,2,4,6,3])
       ị - index into T

K ( Kona ), 23 bytes
A translation of the J answer by Galen
{y@<(#y)#-1_(!x),|!x-1}

MBASIC, 146 159 155 bytes
1 INPUT S$,N:DIM C$(N):P=1:D=1:FOR I=1 TO LEN(S$):C$(P)=C$(P)+MID$(S$,I,1)
2 IF N>1 THEN P=P+D
3 IF P=N OR P=1 THEN D=-D
4 NEXT:FOR I=1 TO N:PRINT C$(I);:NEXT

Updated to handle n=1
Output:
? programmingpuzzles, 5
piermnlsomgzgapzru

? codegolf, 3
cgoeofdl

? elephant, 4
enlatehp

? 1234567, 3
1524637

? WATERMELON, 4
WEAMLTROEN

? qwertyuiop, 1
qwertyuiop

Currently does not suppport case n=1.

Updated to handle case n=1

Saved 4 bytes by cleaning up the compares.

Perl 6, 49 bytes
->\n{*.comb.sort({-abs n-1-$++%(2*n-2||1)}).join}

Try it online!
Takes input as a curried function.
Explanation:
->\n{*.comb.sort({-abs n-1-$++%(2*n-2||1)}).join}
->\n{                                           }  # Take an number
     *.comb        # Turn the string into a list of chars
           .sort({                       })   # And sort them by
                           $++    # The index of the char
                              %(2*n-2||1)  # Moduloed by 2*(n-1) or 1 if n is 0
                       n-1-       # Subtract that from n-1
                   abs            # get the absolute value
                  -               # And negate to reverse the list
                                          .join  # and join the characters

The sequence that it is sorted by looks like this (for n=5):
(-4 -3 -2 -1 0 -1 -2 -3 -4 -3 -2 -1 0 -1 -2 -3 -4 -3 -2 -1)

J, 24 bytes
4 :'x\:(#x)$}:|i:<:y'::[

Try it online!
Explicit dyadic verb. Run it like 'codegolf' f 3.
How it works
4 :'x\:(#x)$}:|i:<:y'::[    x: string, y: height
4 :                         Define a dyadic verb:
               i:<:y        Generate a range of -(y-1) .. y-1
            }:|             Take absolute value and remove last
       (#x)$             1) Repeat to match the string's length
    x\:                     Sort x by the decreasing order of above
                     ::[    If 1) causes `Length Error`, return the input string instead

Normally, explicit function takes additional 5 bytes in the form of n :'...'. But if error handling is added, the difference goes down to 2 bytes due to the parens and space in (tacit)<space>::.

Why I always tend to use sort up?! Your explicit verb is still 3 bytes shorter. Good decision!

APL (Dyalog Classic), 23 bytes
{⍺[⍋(≢⍺)⍴(¯1↓⊢,1↓⌽)⍳⍵]}

Try it online!

Powershell, 99 95 bytes
param($s,$n)$r=,''*$n
$s|% t*y|%{$r[((1..$n+$n..1)*$s.Length|gu)[$i++*($n-gt1)]-1]+=$_}
-join$r

Test script:
$f = {

param($s,$n)$r=,''*$n
$s|% t*y|%{$r[((1..$n+$n..1)*$s.Length|gu)[$i++*($n-gt1)]-1]+=$_}
-join$r

}

@(
    ,("1234567", 3            ,     "1524637")
    ,("qwertyuiop", 1         ,     "qwertyuiop")
    ,("codegolf", 3           ,     "cgoeofdl")
    ,("elephant", 4           ,     "enlatehp")
    ,("programmingpuzzles", 5 ,     "piermnlsomgzgapzru")
) | % {
    $s,$n,$e = $_
    $r = &$f $s $n
    "$($r-eq$e): $r"
}

Output:
True: 1524637
True: qwertyuiop
True: cgoeofdl
True: enlatehp
True: piermnlsomgzgapzru

Explanation
The script:

creates an array of rows,
fills rows with appropriate values,
and returns the joined rows.

The expression ((1..$n+$n..1)*$s.Length|gu
generates a sequence like 1,2,3,3,2,1,1,2,3,3,2,1...
and removes adjacent duplicates. gu is alias for Get-Unique.

For $n=3 the deduplicated sequence is: 1,2,3,2,1,2,3,2,1...
For $n=1 the deduplicated sequence is: 1

The expression $i++*($n-gt1)
returns an index in the deduplicated sequence. =$i++ if $n>1, otherwise =0

Ruby, 75 65 bytes
->s,h{a=['']*h;x=-k=1;s.map{|c|a[x+=k=h-x<2?-1:x<1?1:k]+=c};a*''}

Try it online!
Takes input as an array of chars, returns string
How it wokrs:

Create h strings
For each character in the input string, decide which string to put it in based on its index (the index of the string to be modified goes up until h and then down until 0 and so on)
Return all the strings joined together

64 bytes

@GB it doesn't work for the last case

C, 142 134 bytes
8 bytes saved thanks to Jonathan Frech
Code:
t;i;j;d;f(s,n)char*s;{for(t=strlen(s),i=0;i<n;i++)for(j=0;j+i<t;j=d+i+(n<2))d=j-i+2*~-n,putchar(s[i+j]),i>0&i<n-1&d<t&&putchar(s[d]);}

Explanation:
// C variable and function declaration magic
t;i;j;d;f(s,n)char*s;{
    // Iterate through each "row" of the string
    for(t=strlen(s),i=0;i<n;i++)
        // Iterate through each element on the row
        // Original index iterator here was j+=2*(n-1), which is a full "zig-zag" forward
        // The (n<2) is for the edge case of n==1, which will break the existing logic.
        for(j=0; j+i<t; j=d+i+(n<2))
            // If j+i is the "zig", d is the "zag": Original index was d=j+i+2*(n-i-1)
            // Two's complement swag here courtesy of Jonathan Frech
            d=j-i+2*~-n,
            putchar(s[i+j]),
            // Short circuit logic to write the "zag" character for the middle rows
            i>0 & i<n-1 & d<t && putchar(s[d]);
}

Try it online!

Hello and welcome to PPCG; nice first golf. 134 bytes (assuming GCC).

I have two solutions to the problem. The first solution I did first then I thought of another way to do it that I thought would save bytes but it didn't so I included it anyway.

Solution 1
PHP, 152 144 116 bytes
<?php
for($i=0;$i<$n=$argv[2];$i++)
    for($j=$i;$s=$argv[1][$j];$j+=$n<2|(($f=!$f|!$i)?$i<$n-1?$n+~$i:$i:$i)*2)
        echo $s;

8 bytes thanks to @JoKing
28 bytes thanks to @Shaggy

Try it online!

Solution 2
PHP, 162 bytes
<?php
$s=$argv[0];
$n=$argv[1];
$l=strlen($s);
for($i=0;$i<$l;){
    for($j=0;$j<$n&&$i<$l;)
        $a[$j++].=$s[$i++];
    for($j=$n-2;$j>0&&$i<$l;)
        $a[$j--].=$s[$i++];
}
echo join($a);

Try it online!

You don't need to initialise $f and $n-1-$i can be $n-~$i. 144 bytes

-28 bytes on @JoKing's improvements.

Oop; that breaks when n=1. This one works for the same byte count.

You can also use short tags and remove the space after echo to save 5 more bytes

Charcoal, 21 bytes
⭆ＮΦη¬⌊Ｅ²﹪⁺μ⎇νι±ι∨⊗⊖θ¹

Try it online! Link is to verbose version of code. Works by noting that indices \$ m \$ of the characters on line \$ i \$ satisfy the relation \$ m \pm i = 0 \pmod {2n - 2} \$. Explanation:
 Ｎ                      First input as a number
⭆                       Map over implicit range and join
   η                    Second input
  Φ                     Filter over characters
       ²                Literal 2
      Ｅ                 Map over implicit range
          μ             Character index
             ι ι        Outer index
              ±         Negate
            ν           Inner index
           ⎇            Ternary
         ⁺              Plus
                   θ    First input
                  ⊖     Decremented
                 ⊗      Doubled
                    ¹   Literal 1
                ∨       Logical Or
        ﹪               Modulo
     ⌊                  Minimum
    ¬                   Logical Not
                        Implicitly print

SNOBOL4 (CSNOBOL4), 191 bytes
	S =INPUT
	N =INPUT
	A =ARRAY(N)
	A<1> =EQ(N,1) S	:S(O)
I	I =I + -1 ^ D
	S LEN(1) . X REM . S	:F(O)
	A<I> =A<I> X
	D =EQ(I,N) 1
	D =EQ(I * D,1)	:(I)
O	Y =Y + 1
	O =O A<Y>	:S(O)
	OUTPUT =O
END

Try it online!
Takes S then N on separate lines.
Explanation:
	S =INPUT			;* read S
	N =INPUT			;* read N
	A =ARRAY(N)			;* create array of size N
	A<1> =EQ(N,1) S	:S(O)		;* if N = 1, set A<1> to S and jump to O
I	I =I + -1 ^ D			;* index into I by I + (-1)^D (D starts as '' == 0)
	S LEN(1) . X REM . S	:F(O)	;* extract the first character as X and set S to the
					;* remaining characters, jumping to O when S is empty
	A<I> =A<I> X			;* set A<I> to A<I> concatenated with X
	D =EQ(I,N) 1			;* if I == N, D=1
	D =EQ(I * D,1)	:(I)		;* if I == D == 1, D = 0. Goto I
O	Y =Y + 1			;* increment the counter
	O =O A<Y>	:S(O)		;* concatenate the array contents until last cell
	OUTPUT =O			;* and print
END

Clean, 105 84 bytes
import StdEnv,Data.List
$s n=[c\\l<-[1..n],c<-s&m<-cycle([1..n]++[n-1,n-2..2])|m==l]

Try it online!

JavaScript (Node.js), 83 bytes
c=>g=(s,d=c*2-2,r=d,n=o='')=>s&&([...s].map((c,i)=>i%d%r?n+=c:o+=c),o)+g(n,r-1,r-2)

Try it online!

Pyth, 22 21 bytes
|seMhD,V*lz+PUQP_UQzz

Takes input as n followed by s on separate lines. Try it online here, or verify all the test cases at once here.
|seMhD,V*lz+PUQP_UQzz   Implicit: Q=eval(input()), z=remaining input

             UQ         Range [0-Q)
            P           All but last from the above
                         e.g. for Q=3, yields [0,1]
               P_UQ     All but last of reversed range
                         e.g. for Q=3, yields [2,1]
           +            Concatenate the previous two results
                          e.g. for Q=3, yields [0,1,2,1]
        *lz              Repeat len(z) times
      ,V           z    Vectorised pair the above with z, truncating longer to length of shorter
                          e.g. for Q=3, z=WATERMELON, yields:
                          [[0,'W'],[1,'A'],[2,'T'],[1,'E'],[0,'R'],[1,'M'],[2,'E'],[1,'L'],[0,'O'],[1,'N']]
    hD                  Sort the above by the first element
                          Note this is a stable sort, so relative ordering between equal keys is preserved
  eM                    Take the last element of each
 s                      Concatenate into string
                          Note that if n=1, the result of the above will be 0 (sum of empty array)
|                   z   If result of above is falsey, yield z instead

Edit: saved a byte by moving the empty check to the end of processing. Previous version: seMhD,V*lz|+PUQP_UQ]0z

Red, 153 bytes
func[s n][i: v: m: 1 b: collect[foreach c s[keep/only reduce[v i c]v: v + m
if all[n > 1(i: i + 1)%(n - 1)= 1][m: -1 * m]]]foreach k sort b[prin last k]]

Try it online!
Explanation:
f: func [ s n ] [                      ; s is the string, n is the height
    i: 1                               ; index of the current character in the string
    v: 1                               ; value of the "ladder"
    m: 1                               ; step (1 or -1)
    b: collect [                       ; collect the values in a block b
        foreach c s [                  ; foreach character in the string
            keep/only reduce [ v i c ] ; keep a block of the evaluated [value index char]
            i: i + 1                   ; increase the index
            v: v + m                   ; calculate the value
            if all [ n > 1             ; if height is greater than 1 and
                    i % (n - 1) = 1    ; we are at a pick/bottom of the ladder
                   ]
                [ m: -1 * m ]          ; reverse the step
        ]
    ]
    foreach k sort b [ prin last k ]   ; print the characters in the sorted block of blocks
]

Ruby, 84 bytes
->s,n{r=['']*n;[*0...n,*(2-n)..-1].map{|x|r[x.abs]+=s.slice!(0)||''}while s[0];r*''}

Try it online!

83 bytes