Async programming deep dive, with Bart de Smet
Here is a very informative presentation about the internals of await/async, which makes things a lot clearer when you are trying to understand what the hell is going on there:
Here is a very informative presentation about the internals of await/async, which makes things a lot clearer when you are trying to understand what the hell is going on there:
I'm seeing a pattern here already. After The Expanse, which surprised me with how good the TV show was compared to the books, now The Magicians does the same. There is something to be said about hindsight and when you are adapting a series of books for the small screen you get a lot of resources that the writer himself did not have when he began. I have a feeling that many things that happened in the first season of the TV series will not even happen in the second book. The plot has been changed as well, quite a lot, to the point that now I will be reviewing a book that has at most half of it to do with what you might have seen on TV and another half that you probably won't see even in the future.
I have found a new addiction: prowling StackOverflow and answering questions. It does teach a lot, because you must provide in record time a quality answer that is also appreciated by the person asking the question and by the evil reviewers who hunt you down and downvote you if you mess up. OK, they're not evil, they're necessary. Assholes! :) Anyway, in honor of my 1000th point, I want to share with you the code that I have been working on for one of the questions.public class StrokeAdorner : Adorner
{
private TextBlock _textBlock;
private Brush _stroke;
private ushort _strokeThickness;
public Brush Stroke
{
get
{
return _stroke;
}
set
{
_stroke = value;
_textBlock.InvalidateVisual();
InvalidateVisual();
}
}
public ushort StrokeThickness
{
get
{
return _strokeThickness;
}
set
{
_strokeThickness = value;
_textBlock.InvalidateVisual();
InvalidateVisual();
}
}
public StrokeAdorner(UIElement adornedElement) : base(adornedElement)
{
_textBlock = adornedElement as TextBlock;
ensureTextBlock();
foreach (var property in TypeDescriptor.GetProperties(_textBlock).OfType<PropertyDescriptor>())
{
var dp = DependencyPropertyDescriptor.FromProperty(property);
if (dp == null) continue;
var metadata = dp.Metadata as FrameworkPropertyMetadata;
if (metadata == null) continue;
if (!metadata.AffectsRender) continue;
dp.AddValueChanged(_textBlock, (s, e) => this.InvalidateVisual());
}
}
private void ensureTextBlock()
{
if (_textBlock == null) throw new Exception("This adorner works on TextBlocks only");
}
protected override void OnRender(DrawingContext drawingContext)
{
ensureTextBlock();
base.OnRender(drawingContext);
var formattedText = new FormattedText(
_textBlock.Text,
CultureInfo.CurrentUICulture,
_textBlock.FlowDirection,
new Typeface(_textBlock.FontFamily, _textBlock.FontStyle, _textBlock.FontWeight, _textBlock.FontStretch),
_textBlock.FontSize,
Brushes.Black // This brush does not matter since we use the geometry of the text.
);
formattedText.TextAlignment = _textBlock.TextAlignment;
formattedText.Trimming = _textBlock.TextTrimming;
formattedText.LineHeight = _textBlock.LineHeight;
formattedText.MaxTextWidth = _textBlock.ActualWidth - _textBlock.Padding.Left - _textBlock.Padding.Right;
formattedText.MaxTextHeight = _textBlock.ActualHeight - _textBlock.Padding.Top;// - _textBlock.Padding.Bottom;
while (formattedText.Extent==double.NegativeInfinity)
{
formattedText.MaxTextHeight++;
}
// Build the geometry object that represents the text.
var _textGeometry = formattedText.BuildGeometry(new Point(_textBlock.Padding.Left, _textBlock.Padding.Top));
var textPen = new Pen(Stroke, StrokeThickness);
drawingContext.DrawGeometry(Brushes.Transparent, textPen, _textGeometry);
}
}
public static class Adorningand an example of use:
{
public static Brush GetStroke(DependencyObject obj)
{
return (Brush)obj.GetValue(StrokeProperty);
}
public static void SetStroke(DependencyObject obj, Brush value)
{
obj.SetValue(StrokeProperty, value);
}
// Using a DependencyProperty as the backing store for Stroke. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeProperty =
DependencyProperty.RegisterAttached("Stroke", typeof(Brush), typeof(Adorning), new PropertyMetadata(Brushes.Transparent, strokeChanged));
private static void strokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var stroke= e.NewValue as Brush;
ensureAdorner(d,a=>a.Stroke=stroke);
}
private static void ensureAdorner(DependencyObject d, Action<StrokeAdorner> action)
{
var tb = d as TextBlock;
if (tb == null) throw new Exception("StrokeAdorner only works on TextBlocks");
EventHandler f = null;
f = new EventHandler((o, e) =>
{
var adornerLayer = AdornerLayer.GetAdornerLayer(tb);
if (adornerLayer == null) throw new Exception("AdornerLayer should not be empty");
var adorners = adornerLayer.GetAdorners(tb);
var adorner = adorners == null ? null : adorners.OfType<StrokeAdorner>().FirstOrDefault();
if (adorner == null)
{
adorner = new StrokeAdorner(tb);
adornerLayer.Add(adorner);
}
tb.LayoutUpdated -= f;
action(adorner);
});
tb.LayoutUpdated += f;
}
public static double GetStrokeThickness(DependencyObject obj)
{
return (double)obj.GetValue(StrokeThicknessProperty);
}
public static void SetStrokeThickness(DependencyObject obj, double value)
{
obj.SetValue(StrokeThicknessProperty, value);
}
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeThicknessProperty =
DependencyProperty.RegisterAttached("StrokeThickness", typeof(double), typeof(Adorning), new PropertyMetadata(0.0, strokeThicknessChanged));
private static void strokeThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ensureAdorner(d, a =>
{
if (DependencyProperty.UnsetValue.Equals(e.NewValue)) return;
a.StrokeThickness = (ushort)(double)e.NewValue;
});
}
}
<TextBlock
x:Name="t1"
HorizontalAlignment="Stretch"
FontSize="40"
FontWeight="Bold"
local:Adorning.Stroke="Red"
local:Adorning.StrokeThickness="2"
Text="Some text that needs to be outlined"
TextAlignment="Center"
TextWrapping="Wrap"
Width="600">
<TextBlock.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="Green" />
<GradientStop Offset="1" Color="Blue" />
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
foreach (var property in TypeDescriptor.GetProperties(_textBlock).OfType<PropertyDescriptor>())Here I am enumerating each property of the target (the TextBlock) and checking if they are dependency properties and if they have in their metadata the AffectsRender flag, they I add a property change handler which calls InvalidateVisual on the adorner. Notice that in no part of the code do I remove those handlers. However, at this time I don't think it is a problem. Anyway, the code itself is more about the principles of the thing, rather than the implementation.
{
var dp = DependencyPropertyDescriptor.FromProperty(property);
if (dp == null) continue;
var metadata = dp.Metadata as FrameworkPropertyMetadata;
if (metadata == null) continue;
if (!metadata.AffectsRender) continue;
dp.AddValueChanged(_textBlock, (s, e) => this.InvalidateVisual());
}
Inspired by my own post about simulating F# active patterns in C# and remembering an old crazy post about using try/catch to emulate a switch on types, I came up with this utility class that acts and looks like a switch statement, but can do a lot more. The basic idea was to use a fluent interface to get the same functionality of switch, but also add the possibility of using complex objects as case values or even code conditions. namespace ConstructsI use the static class Do to very easily get a Switch<T> object based on a value, then run actions on that value. The Switch class has a _value field and an _isDone field. When _isDone is set to true, no action is further executed (like breaking from a switch block). The class has the methods Case, and If, as well as OfType and OfStrictType, all of which execute an action if either the value, the function, the condition or the type provided match the initial value. Default is always last, executing an action and setting _isDone to true;
{
public static class Do
{
public static Switch<T> Switch<T>(T value)
{
return Constructs.Switch<T>.From(value);
}
}
public class Switch<T>
{
private bool _isDone;
private T _value;
private Type _valueType;
private Switch(T value)
{
this._value = value;
}
public static Switch<T> From(T value)
{
return new Switch<T>(value);
}
public Switch<T> Case(Func<T> valueFunc, Action<T> action, bool fallThrough = false)
{
if (_isDone) return this;
return Case(valueFunc(), action, fallThrough);
}
public Switch<T> Case(T value, Action<T> action, bool fallThrough = false)
{
if (_isDone) return this;
return If(v => object.Equals(value, v), action, fallThrough);
}
public void Default(Action<T> action)
{
if (_isDone) return;
action(_value);
}
public Switch<T> If(Func<T, bool> boolFunc, Action<T> action, bool fallThrough = false)
{
if (_isDone) return this;
if (boolFunc(_value))
{
action(_value);
_isDone = !fallThrough;
}
return this;
}
private Type getValueType()
{
if (_valueType != null) return _valueType;
if (object.Equals(_value, null)) return null;
_valueType = _value.GetType();
return _valueType;
}
public Switch<T> OfStrictType<TType>(Action<T> action, bool fallThrough = false)
{
if (_isDone) return this;
if (getValueType() == typeof(TType))
{
action(_value);
_isDone = !fallThrough;
}
return this;
}
public Switch<T> OfType<TType>(Action<T> action, bool fallThrough = false)
{
if (_isDone) return this;
if (getValueType() == null) return this;
if (typeof(TType).IsAssignableFrom(getValueType()))
{
action(_value);
_isDone = !fallThrough;
}
return this;
}
}
}
for (var i = 0; i < 25; i++)where the numbers from 0 to 25 are compared with 10, the half of the minutes value of the current time and checked if they are divisible by 7, else they are simply displayed. Note that the first two Case methods receive an optional bool parameter that allows the check to fall through, so that the value is checked if it is equal to 10, but also if it is twice the minute value or divisible by 7. On the other hand, if the value is divisible by 7 it will not display the value in the Default method.
{
Do.Switch(i)
.Case(10, v => Console.WriteLine("i is ten"), true)
.Case(() => DateTime.Now.Minute / 2, v =>
Console.WriteLine($"i is the same with half of the minute of the time ({v})"), true)
.If(v => v % 7 == 0, v => Console.WriteLine($"{v} divisible by 7"))
.Default(v => Console.WriteLine($"{v}"));
}
var f = new Action<object>(x =>
Do.Switch(x)
.OfType<string>(v => Console.WriteLine($"{v} is a string"))
.OfType<DateTime>(v => Console.WriteLine($"{v} is a DateTime"))
.OfType<int>(v => Console.WriteLine($"{v} is an integer"))
.OfType<object>(v => Console.WriteLine($"{v} is an object"))
);
f(DateTime.Now);
f("Hello, world!");
f(13);
f(0.45);
for (var i = 0; i < 100; i++)
{
Do.Switch(i)
.If(v => v % 15 == 0, v => Console.WriteLine($"FizzBuzz"))
.If(v => v % 3 == 0, v => Console.WriteLine($"Fizz"))
.If(v => v % 5 == 0, v => Console.WriteLine($"Buzz"))
.Default(v => Console.WriteLine($"{v}"));
}
Nemesis Games felt like a fixer-upper. The authors had already established a pattern in the books from The Expanse, mainly a psychopathic villain and the motley crew of the Rocinante saving the world through bouts of coincidence and luck that are impossible to believe, and so seeing the exact same formula used again in the fifth book was a disappointment. However, they had another issue: the characters of the story were not very clearly defined. Having hinted since forever that each of the people on the ship had a heavy past, James S.A. Corey decided to explain almost all of those pasts in this book. The fact that the disaster was epic made the book easy to read through, in that "what happens next" trance, but it felt the book version of an elevator show. It even ended badly, with conflicts unresolved and a cliffhanger "to be continued" scene at the end. The sixth book of the series, Babylon's Ashes, is supposed to be published in April this year and seriously I am asking myself if I want to continue reading it.
F# has an interesting feature called Active Patterns. I liked the idea and started thinking how I would implement this in C#. It all started from this StackOverflow question to which only Scala answers were given at the time.// create an active pattern
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, int) -> Some(int)
| _ -> None
// create an active pattern
let (|Bool|_|) str =
match System.Boolean.TryParse(str) with
| (true, bool) -> Some(bool)
| _ -> None
// create a function to call the patterns
let testParse str =
match str with
| Int i -> printfn "The value is an int '%i'" i
| Bool b -> printfn "The value is a bool '%b'" b
| _ -> printfn "The value '%s' is something else" str
// test
testParse "12"
testParse "true"
testParse "abc"
var apInt = new Func<string, Option<int>>(s =>
{
int i;
if (System.Int32.TryParse(s, out i)) return new Option<int>(i);
return Option<int>.Empty;
});
var apBool = new Func<string, Option<bool>>(s =>
{
bool b;
if (System.Boolean.TryParse(s, out b)) return new Option<bool>(b);
return Option<bool>.Empty;
});
var testParse = new Action<string>(s =>
{
var oi = apInt(s);
if (oi.HoldsValue)
{
Console.WriteLine($"The value is an int '{oi.Value}'");
return;
}
var ob = apBool(s);
if (ob.HoldsValue)
{
Console.WriteLine($"The value is an bool '{ob.Value}'");
return;
}
Console.WriteLine($"The value '{s}' is something else");
});
testParse("12");
testParse("true");
testParse("abc");
var apInt = Option<int>.From<string>(s =>
{
int i;
return System.Int32.TryParse(s, out i)
? new Option<int>(i)
: Option<int>.Empty;
});
var apBool = Option<bool>.From<string>(s =>
{
bool b;
return System.Boolean.TryParse(s, out b)
? new Option<bool>(b)
: Option<bool>.Empty;
});
var testParse = new Action<string>(s =>
{
FluidFunc
.Match(s)
.With(apInt, r => Console.WriteLine($"The value is an int '{r}'"))
.With(apBool, r => Console.WriteLine($"The value is an bool '{r}'"))
.Else(v => Console.WriteLine($"The value '{v}' is something else"));
});
testParse("12");
testParse("true");
testParse("abc");
var apInt = FluidFunc.From<string,int>(s =>
{
int i;
return System.Int32.TryParse(s, out i)
? new Tuple<bool, int>(true, i)
: new Tuple<bool, int>(false, 0);
});
var apBool = FluidFunc.From<string,bool>(s =>
{
bool b;
return System.Boolean.TryParse(s, out b)
? new Tuple<bool, bool>(true, b)
: new Tuple<bool, bool>(false, false);
});
var testParse = new Action<string>(s =>
{
FluidFunc
.Match(s)
.With(apInt, r => Console.WriteLine($"The value is an int '{r}'"))
.With(apBool, r => Console.WriteLine($"The value is an bool '{r}'"))
.Else(v => Console.WriteLine($"The value '{v}' is something else"));
});
testParse("12");
testParse("true");
testParse("abc");
public static class FluidFunc
{
public static FluidFunc<TInput> Match<TInput>(TInput value)
{
return FluidFunc<TInput>.With(value);
}
public static Func<TInput, Tuple<bool, TResult>> From<TInput, TResult>(Func<TInput, Tuple<bool, TResult>> func)
{
return func;
}
}
public class FluidFunc<TInput>
{
private TInput _value;
private static FluidFunc<TInput> _noOp;
private bool _isNoop;
public static FluidFunc<TInput> NoOp
{
get
{
if (_noOp == null) _noOp = new FluidFunc<TInput>();
return _noOp;
}
}
private FluidFunc()
{
this._isNoop = true;
}
private FluidFunc(TInput value)
{
this._value = value;
}
public static FluidFunc<TInput> With(TInput value)
{
return new FluidFunc<TInput>(value);
}
public FluidFunc<TInput> With<TNew>(Func<TInput, Option<TNew>> func, Action<TNew> action)
{
if (this._isNoop)
{
return this;
}
var result = func(_value);
if (result.HoldsValue)
{
action(result.Value);
return FluidFunc<TInput>.NoOp;
}
return new FluidFunc<TInput>(_value);
}
public FluidFunc<TInput> With<TNew>(Func<TInput, Tuple<bool,TNew>> func, Action<TNew> action)
{
if (this._isNoop)
{
return this;
}
var result = func(_value);
if (result.Item1)
{
action(result.Item2);
return FluidFunc<TInput>.NoOp;
}
return new FluidFunc<TInput>(_value);
}
public void Else(Action<TInput> action)
{
if (this._isNoop) return;
action(_value);
}
}

Cibola Burn is the book that worried me the most. James S.A. Corey had created a world in which the Solar System has been colonized and Abaddon's Gate, the third book in the Expanse series, had ended with humanity gaining access to one thousand new star systems. I liked the Solar System background and I really thought the fourth book was gonna suck. Well, while being some of the same old thing as the other books and maybe even better written - so a better book - it also sucked because I could easily imagine Picard and The Enterprise going on a mining colony to settle a territorial dispute and, beside being PG-13, having almost nothing changed.
The plot of the book is about Holden and the Rocinante being sent to mediate a situation between the representatives of an Earth corporation and the people who had landed on the planet before the corporation had even filed a claim. You have your familiar characters like the crew of the Rocinante and Miller and even Havelock (Miller's former partner, now a security employee of the Earth corporation), you have your psychotic leader types that mess everything up while the good guys hesitate to just shoot them, you have the very human characters with children that need to be saved, you have the overwhelming but dumb alien presence and the snowballing crisis that drives it all. I thought the story was a bit of a rehashing of the same ideas and therefore I enjoyed it less than I would have if I had read it standalone. I know that successful series are based on successful books and must present kind of the same so to not alienate its readers, but as the intergalactic situation changes dramatically, damn if I don' feel the plot should vary a little, too.
Given that science and technology have always played a big part in the Expanse, you get to see more attention to the details than from other authors, but so far Cibola Burn felt to me like the least scientifically accurate so far. And yet I liked it, because it is well written and it drives the reader through the story and makes most characters likable and one wonders what the hell would they do if they were in the character's shoes.
Cibola is the Spanish transliteration of a native name for a pueblo (Hawikuh Ruins) conquered by Francisco Vásquez de Coronado, also one of the seven mythical gold cities that the conquistadors searched for in vain.

I've been monitoring more closely the access to my blog and I noticed that a lot of people are interested in the post about the Sicilian Wing Gambit, defined as pushing b4 in reply to the standard Sicilian Defense e4 c5. So I will be trying in this to use new knowledge and computer engines to revisit this funky opening gambit. As such I will be using LiveBook, a system created by the people at ChessBase that tries to catalog and discover chess based on active chess games and analysis, as well as computer engines, in this case Komodo 9 with a 256MB table memory. I've continued each variation until there was only 1 game left in the database, then I stopped.
Let's start with LiveBook. Here is a PGN with the main variations in order of use. You will notice that the main line is to accept the gambit (GM Jan Gustafsson even wrote "take the pawn and be happy!" at that particular junction), then refuse the second pawn and immediately challenge the center - which would have been the Sicilian idea all along - by pushing d5. It loks a bit like a Scandinavian Defense, but without White being able to push the Black queen back with Nc3. The main line shows Black gaining advantage, but then losing it by move 12, where equality sets in. However, the computer does not recognize some of the moves in the main line as best.
In the line that I was interested in, the one where Black takes the pawn on the a-file, White gains the classical center and technically it is ahead in deployment of minor pieces, if one considers a knight on the a-file and a semi blocked in bishop developed pieces. However, not all is lost, as the computer has some ideas of its own. Also keep in mind that the Sicilian Wing Gambit is not well known and few people actively employ it.
So here is the PGN of the LiveBook database, based on what people played: 1. e4 c5 2. b4 {This is the Sicilian Wing Gambit. From here on, the percentages in the comments are wins for White and the points are from computer engines.} cxb4 (2... b6 3. bxc5 bxc5 4. Nc3 Nc6 5. Rb1 g6 6. g3 Bg7 7. Bg2 Ba6 8. Nge2 {50% / 0.00}) 3. a3 d5 (3... bxa3 4. Nxa3 d6 5. d4 Nf6 6. Bd3 Nc6 7. c3 e5 8. Ne2 {50% / -0.33}) (3... e6 4. axb4 Bxb4 5. Bb2 (5. c3 Be7 6. d4 d6 7. Bd3 Nf6 8. Ne2 Nc6 9. O-O O-O) 5... Nf6 6. e5 Nd5 7. c4 Ne7 8. Na3 (8. Qg4 Ng6 9. h4 h5 10. Qg3 Nc6 11. Bd3 {50% / -0.75}) 8... Nbc6 9. Nc2 Ba5 {50% / -0.25}) 4. exd5 Qxd5 5. Nf3 (5. Bb2 e5 6. Nf3 Nc6 7. c4 Qe6 8. Bd3 Nf6 9. O-O
Bd6 10. Re1 O-O 11. axb4 Nxb4 12. Bf1 {75% / 0.00}) 5... e5 6. Bb2 (6. axb4 Bxb4 7. c3 Be7 8. Na3 Nc6 9. Nb5 Qd8 10. d4 exd4 11. Bf4 Kf8 12. Nbxd4 Nxd4 13. Nxd4 {50% / 0.00}) (6. c4 Qe6 7. Bd3 Nc6 8. Bb2 Nf6 9. O-O Bd6 10. Re1 O-O 11. axb4 Nxb4 12. Bf1 {75%}) 6... Nc6 7. c4 Qe6 8. Bd3 Nf6 9. O-O Bd6 10. Re1 O-O 11. axb4 Nxb4 12. Bf1 e4 13. d3 Qd7 14. dxe4 Bc5 15. Bxf6 Qxd1 16. Rxd1 gxf6 {50% / 0.00} *
Now let's put Komodo on the job, let us know what is going on here. Many people analysed the position resulting after pushing b4 and with depths of 36 and 40, computer engines overwhelmingly suggest taking the pawn. However we might want to explore what happens if we take another option. It is interesting to note that Komodo 9 pushes the main move as the third most important at depth 24. Perhaps later on this would get reversed again, but this soon into the game it just tells us that the other options are equally good. The two moves I am talking about is d5 and e5. Interestingly enough, the second most common human move (b6) is not even on the radar for the computer, while the computer move appears to have been played only 4 times by humans. So let's take a look at computer moves:1. e4 c5 2. b4 d5 3. exd5 cxb4 4. a3 Qxd5 5. Nf3 e5 6. axb4 Bxb4 7. c3 e4 8. cxb4 exf3 9. Qxf3 Qxf3 10. gxf3 * At the end of all this, White has four pawn islands and doubled pawns, but can quickly use the semi open files to attack with rooks. Maybe this discourages you, but remember two things: these are computers making these moves and while the position looks weird, you get attacking chances with no loss of material. That is the purpose of a gambit after all.
Let's see what computers say about the line that we want to happen. The gambit is accepted, the a-pawn is captured as well. What then? I was surprised to see that, depending on depth and engine, the next move is quite different. Stockfish 6, at depth 39 goes with d4, taking control of the center and ignoring the Black a-pawn. The variations from this position are quite complex and have less to do with this gambit. I would gamble (pardon the pun) that the purpose of the wing gambit was reached at this point. Computers give a clear equality between players, but remember that even after we capture the a-pawn, we have still would be a pawn down. Black is forced to passive moves like e6, d6, having to spend resources to regain center control, while most White pieces have clear attack lines.
An example: 1. e4 c5 2. b4 cxb4 3. a3 bxa3 4. d4 e6 5. Nf3 d5 6. e5 Bd7 7. Bd3 Nc6 8. Nxa3 Bb4+ 9. Bd2 Bxd2+ 10. Qxd2 a6 *
But what happens in between these two options? What if Black accepts the gambit, but doesn't take the second pawn? Will the computer see the same result as in the "human main line" we first discussed? Not quite. The computer moves are really different from the human ones. 1. e4 c5 2. b4 cxb4 3. a3 e5 4. Nf3 Nc6 5. Bb2 Nf6 6. Nxe5 Qe7 7. Nf3 Nxe4 8. Be2 d5 9. O-O Qd8 10. Bb5 bxa3 11. Nxa3 Bc5 * The result is another equal position, where White lost the center, but has a strong, yet weird development.
Also check out 3... d5: 1. e4 c5 2. b4 cxb4 3. a3 d5 4. exd5 Qxd5 5. Nf3 e5 6. axb4 Bxb4 7. c3 e4 8. cxb4 exf3 9. Qxf3 Qxf3 10. gxf3 Ne7 * An interesting tactic is not to take the d5 pawn and instead advance the e-pawn to e5: 1. e4 c5 2. b4 cxb4 3. a3 d5 4. e5 Nc6 5. Bb2 Qb6 6. Nf3 Bg4 7. axb4 Qxb4 8. Bc3 Qe4+ 9. Be2 Bxf3 10. gxf3 Qf4 11. d4 * You can watch an example game in this variation from Kingscrusher.
My opinion on this is that White forces a strong center, but, as seen from the computer variation, the sides get seriously compromised. The truth is that I always wondered if there is a solid play with the king in the center. This might be it, although keep in mind that in that position White is a pawn down.
Well, that's easy to answer: it's another opening :) called the Polish or Sokolsky opening and I have written another blog post about it, although it is pretty old. Maybe I will also revisit that one. The point with that opening is that it already shows Black what we plan and it has some other principles of work, more closely related to the English opening to which it sometimes transposes. The Wing Gambit, though, is a response to the Sicilian, trying to pull the opponent from their comfort zone and into ours.
One can wait for the wing gambit until knights have left their castle. That's called the Portsmouth Gambit (1. e4 c5 2. Nf3 Nc6 3. b4) and some consider it stronger than the gambit presented here. It might be interesting to analyse. Haven't found a lot of resources on it, just this 2014 book from David Robert Lonsdale.
Another option is to play 3. a3, preparing a support of b4 on the next move. It does produce similar results as the base gambit, but I didn't have time to analyse it and it feels a bit slow, to be honest.
Defending c5 with b6 leads to a Sicilian without the b-pawns. That means that an attack on the queen side is out and the White light square bishop can linger around on the queen side as long as it wants, targeting that juicy Black king side from afar. Combine that with the dark square bishop having a nice diagonal as well. 1. e4 c5 2. b4 b6 3. bxc5 bxc5 4. Nf3 Nc6 5. Bb5 Nd4 6. Nxd4 cxd4 *
For an example of that variation, check out Kingscrusher's video.
I couldn't find a lot of traps in the Sicilian Wing Gambit. One good video on this comes from GJ_Chess (ignore his Indian accent, he is actually quite good and, what I like a lot, he focuses on traps and dirty tricks in his videos):
[youtube:5KNVTce4tmE]
The ECO category for this opening is B20, same as for the Sicilian Defense, which doesn't help a lot.
After Black captures the b-pawn, c4 is called the Santasiere variation of the Wing Gambit, and Black's only option seems to try for control of d4 with either e5 or Nc6. Taking en-passant is giving White a lot of compensation in development.
The goading of the Black pawn with a3 that we've covered above is called the Marshall variation. If Black pushes the pawn to d5 and White captures, we enter the Marienbad variation, while if Black captures the a-pawn, it is called Carlsbad variation. For the record, bad is the German name for bath not an indication that the variation is bad :).
Pushing a3 before b4 is called the Mengarini gambit. Check out a game from 2013 between Dobrov and Blom.
A nice review of this gambit, with names of the variations and some human analysis, can be found on chess.com. (The same author has a post on The Portsmouth Gambit as well)
On chesstempo there is a nice list of games with this variation that you can search. Play around with the Advanced Search parameters. As a reference, there are 11 games from people over 2500 using the Wing Gambit and White has most wins. If restricting to games after 2000, you only get one, which ended in a draw, although White was better at the end. You can see the game here: Timur Gareev vs Gata Kamsky, US Championship (2015).
Interesting Wing Gambit/Fried Liver attack combo that you can see here: John P. Pratt vs Elden Watson, 29 Sep 1976, Hill Air Force Base, Utah. You gotta love that. Not the best rated players in the world, but still. Here is a lichess computer analysis of the same game.
A 2013 call to reevaluate the Wing Gambit with some examples of famous old games where the opening had a strong effect.
Unlike the Sokolsky opening, the purpose of the Sicilian Wing Gambit is not to push the b-pawn to block Black's development, but to deflect the c-pawn from protecting the center. The goal is reached when White has a strong center. In no way does it mean it is a winning gambit. There are no brutal traps, no quick wins, the only purpose of this opening is to pull an aggressive Sicilian player from their comfort zone and into a slower, more positional one. That means that the White player needs to attack like crazy until Black is a mere smear on the board, otherwise the center control and speed advantage that may be obtained from the opening can be easily lost.
From my analysis I gather that Black should accept the gambit, but not continue to take pawns like the a-pawn, instead focusing on their own piece development and control of the center. With perfect computer play, equality is reached and maintained. White often fianchettoes the dark square bishop to b2 from where they put pressure on the Black king side. Black's game often centers on exchanging pieces, so that the opening advantage gets lost. The best chance Black has to decline the gambit seems to be pushing d5, going into murky territory.
White on the other hand should push for the center, even propping the d-pawn with c3 and blunting the dark square bishop diagonal. Then focusing on attack is the most important feature, as in most gambits.
Careful with the variation in which Black attacks the e4 pawn with d5, then capturing with the queen after the exchange. If not careful the queen can fork the king and the rook. That is why Nf6 is played by White as the next move or even Bb2, although that's not as good, as the bishop can be deflected.
Usually the a-pawn is recaptured with the knight, not the bishop. This may seem surprising, but what it prepares is moving the knight on b5, attacking c7 and a7 and being very hard to dislodge, as the a-pawn is pinned to the rook. Some variations sacrifice the Black rook in the corner for a quick counter-attack. In case it is captured with the bishop, the idea is to exchange dark square bishops and prevent the Black king from castling.
In several games I have seen, moving towards the center forces Black to use e6 followed by d5, to which White can respond with e5 themselves and get into French defense territory. Personally I dislike playing against the French, but in this case, without b-pawns, the theory is quite different as well. For an example, check out this video from Kingscrusher.
Even if caustic GM Roman Dzindzichashvili categorized this as the worst opening for White, don't forget that it was used by Fischer in 1992 to beat Spassky. Well, a transposition thereof. If you are confident in your chess skills, this is just as good an opening as any other and at least you need to know it a little in order to defend against it.
I would love some comments from some real chess players, as all of this is based on game databases and computer analysis. Please leave comments with what you think.
Here is Simon Williams using the gambit against a young Polish player:
[youtube:NkD12gJcbO4]
ChessTrainer shows a nice game where he uses the gambit to get a quick center and take his opponent out of Sicilian main lines:
[youtube:KITg5dYVsYk]
MatoJelic is showing us some classical games:
Thomas A vs Schmid, Hastings 1952
[youtube:8UTSqKIPQ-8]
Wood L vs Mease A ,USA, 1949
[youtube:XkymLpg781c]
Coma is my favorite Romanian bands and I've known them almost since they were formed. They have been singing for 16 years now and it was nice to see the concert room filled with people of all ages, including a 16 year old boy who had his birthday on the same day. For me this concert was a double whammy, as the lead singer of one of the opening bands is a former colleague of mine. Yeah, small world.
The authors known as James S.A. Corey have planned the series of books The Expanse to have three volumes. As such, Abaddon's Gate feels like a wraparound of the stories so far, while also remaining a good standalone feature. However, because of the overwhelmingly positive response for the series, it was continued for another three volumes, and now three more are announced. There are also various novellas in between books. That is a problem, since this book ends up undoing what the first two started. But let's not get ahead of ourselves here, just be warned that this review may contain spoilers, without which I couldn't possibly comment on the plot of the book.
Hi, all!
The second book of The Expanse is much better written than the first, however the story is a little weaker. There are more of the details one would already be familiar with from the TV show, the character of Chrisjen Avasarala is introduced and chapters are written from the perspective of many more characters than just Miller and Holden. That means there are many more chances to completely dislike a chapter if you hate the character. For me, that character was Prax, someone who would endanger everybody and himself with random emotional outbursts. Perhaps he was put there just to offset the slightly similar behavior of Holden, who now looks like the paragon of professionalism in comparison.
I absolutely love the TV series The Expanse and so, after ten torturous weeks in which I would alternatively get filled with joy at the release of another episode, then fall into despair when it ended, I've decided to cut out the middleman and read the damn book. And now that I've read it, I have to say that I am really glad to have seen the show first. It's not that it is a bad book - it is not, it's very good - but the show is better and having the characters in the series blend with the ones on the page is turning them into something more fleshed out, more complex. And I am not the only one thinking that.
If there is anything that I am forced to say about Adrian Despot, the frontman of Vita de Vie, is that he is a true artist. The concert tonight was spot on, even if I am not a fan of the band. The guest bands were pretty good, too, but I have to admit that for most of them I was waiting for them to stop playing so I can listen to the great playlist from DJ Hefe. The audience was really mixed, ranging from little kids to old people. It felt great to see all these people singing along and reliving some of the greatest hits of the band.
In the previous post I was discussing Firebase, used in Javascript, and that covered initialization, basic security, read all and insert. In this post I want to discuss about complex queries: filtering, ordering, limiting, indexing, etc. For that I will get inspiration (read: copy with impunity) from the Firebase documentation on the subject Retrieving Data, but make it quick and dirty... you know, like sex! Thank you, ma'am!ref.child('user')
.once('value',function(snapshot) {
var users=[];
snapshot.forEach(function(childSnapshot) {
var item=childSnapshot.val();
if (/adam/i.test(item.name)) {
users.push(item.userId);
}
});
getInvoiceTotalForUsers(users,DoSomethingWithSum);
});
function getInvoiceTotalForUsers(users,callback)
{
var sum=0;
var count=0;
for (var i=0; i<users.length; i++) {
var id=users[i];
ref.child('invoice')
.equalTo(id,'userId')
.orderByChild('price')
.startAt(10)
.endAt(100)
.once('value',function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var item = childSnapshot.val();
sum+=item.price;
count++;
if (count==users.length) callback(sum);
});
});
}
}