private static string ReadAsUtf8String(ImmutableByteWrapper bulkEdits)
 {
     byte[] asRead = new byte[bulkEdits.Length];
     for (int i = 0; i < bulkEdits.Length; i++)
     {
         asRead[i] = bulkEdits.ReadAddress(new ByteAddress(i));
     }
     return Encoding.UTF8.GetString(asRead);
 }
        public ImmutableByteWrapper WriteToAddress(ByteAddress address, byte newValue)
        {
            ValidateAddress(address);
            ImmutableByteWrapper retval = new ImmutableByteWrapper();
            retval.InitialState = this.InitialState; //it's immutable, so every instance can share a pointer to it.
            retval.edits = new Dictionary<ByteAddress, byte>(this.edits);
            retval.edits[address] = newValue;

            return retval;
        }
 public ImmutableByteWrapper WriteMultipleAddress(IEnumerable<Tuple<ByteAddress, byte>> writes)
 {
     ImmutableByteWrapper retval = new ImmutableByteWrapper();
     retval.InitialState = this.InitialState;
     retval.edits = new Dictionary<ByteAddress, byte>(this.edits);
     foreach(var toWrite in writes)
     {
         ValidateAddress(toWrite.Item1);
         retval.edits[toWrite.Item1] = toWrite.Item2;
     }
     return retval;
 }
 public static GameMemory OpenFile(string filename)
 {
     byte[] file = ReadFile(filename);
     int length = file.Length;
     if(length < headerSize)
     {
         throw new ArgumentException($"{filename} is not a valid story file");
     }
     ImmutableByteWrapper wrapper = new ImmutableByteWrapper(file);
     byte high = wrapper.ReadAddress(Bits.AddressOfHighByte(baseOffset));
     byte low = wrapper.ReadAddress(Bits.AddressOfLowByte(baseOffset));
     int dynamicSegmentLength = (256 * high) + low;
     if(dynamicSegmentLength > length)
     {
         throw new ArgumentException($"{filename} is not a valid story file");
     }
     byte[] dynamicPart = new byte[dynamicSegmentLength];
     Array.Copy(file, dynamicPart, dynamicSegmentLength);
     byte[] staticPart = new byte[length - dynamicSegmentLength];
     Array.Copy(file, dynamicSegmentLength, staticPart, 0, length - dynamicSegmentLength);
     return new GameMemory(staticPart, new ImmutableByteWrapper(dynamicPart));
 }
 public void WriteByte(ByteAddress address, byte toWrite)
 {
     this.dynamicState = this.dynamicState.WriteToAddress(address, toWrite);
 }
 public GameMemory(byte[] staticMemory, ImmutableByteWrapper dynamicState)
 {
     this.lengthOfDynamicState = dynamicState.Length;
     this.staticMemory = staticMemory.ToImmutableArray<byte>();
     this.dynamicState = dynamicState;
 }
        private static void TestReadingAndWritingGameState()
        {
            string storyState = "Listen to a story 'bout a guy named Al";
            string interpreterState = "Who lived in the sewer with his hamster pal";
            int expectedImmutableSize = Encoding.UTF8.GetByteCount(storyState);
            byte[] immutablePart = Encoding.UTF8.GetBytes(storyState);
            int expectedDynamicSize = Encoding.UTF8.GetByteCount(interpreterState);
            byte[] dynamicPart = Encoding.UTF8.GetBytes(interpreterState);
            ImmutableByteWrapper changingPart = new ImmutableByteWrapper(dynamicPart);
            GameMemory gameState = new GameMemory(immutablePart, changingPart);

            WordAddress zeroPointer = new WordAddress(0);
            List<WordAddress> firstFivePointers = Enumerable.Range(0, 5)
                .Select(el => zeroPointer + el)
                .ToList();

            IEnumerable<ByteAddress> firstTenByteAddress = Enumerable.Range(0, 10)
                .Select(el => new ByteAddress(el));

            byte[] firstTenBytes = firstTenByteAddress
                .Select(p => gameState.ReadByte(p))
                .ToArray();

            byte[] firstFiveWords = firstFivePointers
                 .Select(p => gameState.ReadWord(p))
                 .SelectMany(word => new byte[] { (byte)(word.Value & 0xFF), (byte)((word.Value >> 8) & 0xFF) })
                 .ToArray();

            string firstFive = new string(Encoding.UTF8.GetChars(firstFiveWords));

            byte[] firstFiveTransposed = firstFivePointers
                .Select(p => gameState.ReadWord(p))
                .SelectMany(word => new byte[] { (byte)((word.Value >> 8) & 0xFF), (byte)(word.Value & 0xFF) })
                .ToArray();

            string firstFiveTransposedRead = new string(Encoding.UTF8.GetChars(firstFiveTransposed));

            string whatIRead = new String(Encoding.UTF8.GetChars(firstTenBytes));

            //test writing to game state:
            //replace "Who lived " with
            //        "zzCheese2 "
            byte[] toWrite = Encoding.UTF8.GetBytes("zzCheese2 ");
            System.Diagnostics.Debug.Assert(toWrite.Length == firstTenBytes.Length);

            Enumerable.Range(0, 10)
                .Select(el => new { address = new ByteAddress(el), val = toWrite[el] })
                .ToList()
                .ForEach(item => gameState.WriteByte(item.address, item.val));

            byte[] firstTenBytesAgain = firstTenByteAddress
                .Select(p => gameState.ReadByte(p))
                .ToArray();

            string mutated = new string(Encoding.UTF8.GetChars(firstTenBytesAgain));

            //test writing Words instead of bytes to game state:
            byte[] toWriteWordWise = Encoding.UTF8.GetBytes("aaZZeess33");
            WordAddress zero = new WordAddress(0);
            for(int i = 0; i < toWriteWordWise.Length - 1; i += 2)
            {
                int val = (toWriteWordWise[i] * 256 + toWriteWordWise[i + 1]);
                WordAddress address = zero + i / 2;
                gameState.WriteWord(address, new Word(val));
            }

            byte[] firstTenBytesAgain2 = firstTenByteAddress
                .Select(p => gameState.ReadByte(p))
                .ToArray();

            string mutated2 = new string(Encoding.UTF8.GetChars(firstTenBytesAgain2));

            Console.WriteLine($"{whatIRead} became {mutated} and then {mutated2}");
        }
        private static void TestMemoryBase()
        {
            byte[] bytes = Encoding.UTF8.GetBytes("Hello World");
            ImmutableByteWrapper initialState = new ImmutableByteWrapper(bytes);
            ByteAddress address1 = new ByteAddress(1);
            ImmutableByteWrapper edited = initialState.WriteToAddress(address1, 0x00);
            //owerwrite "world"
            byte[] newBytes = Encoding.UTF8.GetBytes("woid!");
            IEnumerable<int> sixThruTen = Enumerable.Range(6, 5);
            var multiEdits = sixThruTen.Select(val =>
            {
                var address = new ByteAddress(val);
                var toWrite = newBytes[val - 6];
                return new Tuple<ByteAddress, byte>(address, toWrite);
            });
            ImmutableByteWrapper bulkEdits = initialState.WriteMultipleAddress(multiEdits);
            var toPrint = ReadAsUtf8String(bulkEdits);
            var oldToPrint = ReadAsUtf8String(initialState);
            Console.WriteLine($"Initial state has {oldToPrint} and bulk edited has {toPrint}");

            int length = initialState.Length;
            Console.WriteLine(edited.ReadAddress(address1));
            Console.WriteLine(initialState.ReadAddress(address1));
        }