Forth: 22 / 32 Chars. ( Using gforth )
s" Happy 2022" swap +! ( 22 chars )
A compiled but slightly longer version is shown here,
as the result can be decompiled, and its effects demonstrated.
Principle of operation is identical to the shorter version above.
Consider the longer version as demonstratable proof of concept.
: happy s" Happy 2022" swap +! ; ( 32 chars )
I'll decompile this first, then execute, and decompile it again:
see happy ( decompile happy, as it is now )
: happy
s" Happy 2022" swap +! ;
happy ( execute the self-modifying happy )
see happy ( decompile happy again )
: happy
s" Rappy 2022" swap +! ;
What happens here is:
s" ..." places two items on stack: address of first string character, and string length, which is 10 in this case. The sequence "swap +!" adds string length, 10, to the contents at first string address, thereby changing ASCII H to ASCII R.
A version matching the requirements slightly better is compiled using vfx:
: h 1 s" Happy 2021" 2dup type + 1- c+! ;
reason for using a different compiler for this version is that it provides c+! already which I'd have to add to gforth first, which should introduce a penalty of about 22 chars.
disassembling first:
( 080C0AF0 E81383F9FF ) CALL 08058E08 (S") "Happy 2021"
( 080C0B00 8BD3 ) MOV EDX, EBX
( 080C0B02 035D00 ) ADD EBX, [EBP]
( 080C0B05 4B ) DEC EBX
( 080C0B06 800301 ) ADD Byte Ptr 0 [EBX], 01
( 080C0B09 8BDA ) MOV EBX, EDX
( 080C0B0B FF15B9DE0408 ) CALL [0804DEB9] TYPE
( 080C0B11 C3 ) NEXT,
executing several times, output follows immediately:
h Happy 2022
h Happy 2023
h Happy 2024
h Happy 2025
h Happy 2026
disassembling again:
( 080C0B30 E8D382F9FF ) CALL 08058E08 (S") "Happy 2026"
( 080C0B40 8BD3 ) MOV EDX, EBX
( 080C0B42 035D00 ) ADD EBX, [EBP]
( 080C0B45 4B ) DEC EBX
( 080C0B46 800301 ) ADD Byte Ptr 0 [EBX], 01
( 080C0B49 8BDA ) MOV EBX, EDX
( 080C0B4B FF15B9DE0408 ) CALL [0804DEB9] TYPE
( 080C0B51 C3 ) NEXT,