protected async Task WaitAfterPlaced(PixelReturnData response, bool placed)
        {
            int      expectedCooldown = placed ? canvas.PlaceCooldown : canvas.ReplaceCooldown;
            bool     cdIsTooLong      = Math.Max(expectedCooldown, 1000) / Math.Max(response.CoolDownSeconds, 1D) < 1000;
            TimeSpan timeToWait;

            if (cdIsTooLong)
            {
                //accumulate time when CD is long to use it efficiently when CD becames normal
                timeToWait = TimeSpan.FromMilliseconds(Math.Min(response.Wait - 1000, 30000 + 4 * canvas.ReplaceCooldown));
            }
            else if (response.Wait > canvas.TimeBuffer)
            {
                timeToWait = TimeSpan.FromSeconds(response.CoolDownSeconds);
            }
            else
            {
                timeToWait = TimeSpan.FromMilliseconds(canvas.OptimalCooldown);
            }

            await Task.Delay(timeToWait, finishToken);
        }
 protected async Task ProcessPlaceFail(PixelReturnData response)
 {
     logger.LogDebug($"PerformBuildingCycle: return code {response.ReturnCode}");
     if (response.ReturnCode == ReturnCode.Captcha)
     {
         if (options.CaptchaTimeout > 0)
         {
             ProcessCaptchaTimeout();
         }
         else
         {
             ProcessCaptcha();
         }
     }
     else
     {
         logger.LogFail(response.ReturnCode);
         if (response.ReturnCode == ReturnCode.IpOverused)
         {
             await Task.Delay(TimeSpan.FromMilliseconds(Math.Min(response.Wait - 1000, 55000)), finishToken);
         }
         throw new Exception("critical error when trying to place");
     }
 }